PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Hardware-I2C klappt nicht



Murus
30.07.2007, 16:53
Hallihallo zusammen,

ich möchte bei einem Mega8 das hardwareseitige I2C-Interface benutzen.
Momentan läuft die Sache mit dem Software-I2C von Bascom. Dies möchte ich ändern und stattdessen den Code umschreiben, damit das AVR interne Modul verwendet wird.
Angesprochen wird ein Temperatursensor (DS1631Z)

Leider funktioniert die Hardware-Version nicht und ich weiss nicht wieso, hier die Codes:

Auszug aus dem funktionierenden Code:



' I2C initialisieren:
Config Scl = Portc.5 'Is serial clock SCL
Config Sda = Portc.4
Const Schreiben = &B10010000 ' Schreibadresse
Const Lesen = &B10010001 ' Leseadresse
.
.
.
' Thermometer soll Temperatur messen:
I2cstart
I2cwbyte Schreiben
I2cwbyte &H51 ' Befehl fürs Temperaturmessen schicken
I2cstop

Waitms 30

' Thermometer soll Temperatur an den AVR schicken:
I2cstart
I2cwbyte Schreiben
I2cwbyte &HAA 'Temperaturausgabe-Befehl schicken
I2cstart
I2cwbyte Lesen
I2crbyte Twidaten1 , Ack
I2crbyte Twidaten2 , Nack
I2cstop


Dieser Code funktioniert wunderbar, die beiden Bytes Twidaten1 und Twidaten2 enthalten dann die Temperatur des Thermometers. Die Routine zum Ansprechen des Temperatursensors stimmt auch, das klappt also.

Dieses Vorgehen möchte ich nun mit der Hardware-Lösung machen. Hier das momentane, leider nicht funktionale Listing:



'Das I2C-Interface initialisieren:
Twsr = &B00000000 ' Prescaler = 1
Twbr = 10 ' Frequenz des TWi: 10,4kHz
.
.
.
'Befehl zum Messen ausgeben:
Twcr = &B10100100 ' Start erzeugen
Gosub Twiloopsub ' Warten bis fertig

Twdr = &B10010000 ' Slave Adresse senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

Twdr = &B1010001 ' Befehl zum Messen senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

Twcr = &B10010100 ' Stop

Waitms 30

'Temperaturwert einlesen:

Twcr = &B10100100 ' Start erzeugen
Gosub Twiloopsub ' Warten bis fertig

Twdr = &B10010000 ' Slave Adresse senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

Twdr = &B10101010 ' Befehl senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

Twcr = &B10100100 ' Erneut Start erzeugen
Gosub Twiloopsub ' Warten bis fertig

Twdr = &B10010001 ' Slave Adresse senden (lesen)
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

Twcr = &B11000100 ' Ack erzeugen
Gosub Twiloopsub
Twidaten1 = Twdr

Twcr = &B10000100 ' Nack erzeugen
Gosub Twiloopsub
Twidaten2 = Twdr

Twcr = &B10010100 ' Stop

' Twidaten1/2 enthalten den Temperaturwert.

Twiloopsub:
Do : Loop Until Twcr.twint = 1 ' Dann ist TWI-Aktion beendet.

Return


Diese beiden Listings sollten genau das Gleiche machen, dem Sensor sagen, er soll messen und dann den Wert abrufen.
Nur klappt das mit der zweiten Variante gar nicht.
Twidaten1 enthält immer den Wert 196 und Twidaten2 den Wert 0. Eine Kommunikation zum Sensor findet also nicht statt.

Sieht jemand gerade eine Lösung für dieses Problem?

Herzlichen Gruss und vielen Dank

Mario

linux_80
30.07.2007, 22:17
Hallo,

wenn du von Soft-I2C auf Hardware umstellen möchstest, und schon einen Code hast der läuft, geht das am einfachsten in dem man die I2C-TWI-Lib einbindet:
Diese Zeile irgendwo am Anfang des Programms:

$lib "i2c_twi.lbx"

Schon wird die Hardware verwendet, und man kann die gewohnten Bascom-Befehle für I2C verwenden.

Was man dann aber noch angeben muss, ist die Geschwindigkeit, und damit die Register initialisiert werden:

Config Twi = 400000
' auch am Anfang irgendwo

Man muss ja nicht immer alles neu erfinden ;-)

Murus
30.07.2007, 22:28
Hmmm... eigentlich hab ich gedacht, ich lese halt mal das DB und strick mir so was zusammen. Möchte von diesen Bascom-Highlevel-Befehlen etwas loskommen.
Deshalb wäre es super, direkt die AVR-Register zu konfigurieren... Es kommt mir mehr auf den Lerninhalt darauf an, als auf die Funktion.

Deshalb wäre es super, wenn das auch noch funktionieren würde. Ich mache mal ein anderes Programm, welches die Statuscodes des AVRs noch auswertet, dann seh ich mal, wo es klemmt.
Der Hardware-Code sollte doch stimmen, oder? Oder vergesse ich etwas? Die Subroutine hab ich gemacht, um Speicher zu sparen. Sie sollte auch angesprungen werden, habe swstack, hwstack und framesize auf 128 gesetzt.
Achja: der AVR läuft intern mit 1MHz, die I2C-Frequenz ist 10,4kHz.

Herzlichen Gruss
Mario

linux_80
30.07.2007, 22:40
Schau mal ins Wiki, erst bei TWI dann auch die Seite TWI-Praxis,
da hab ich mal was vorbereitet ;-)
Für Slave und Master.

Speichersparen würde ich erst anfangen wenns läuft, dann kann man an den verschiedenen Stellen zum optimieren anfangen.

Murus
31.07.2007, 00:42
Ok, hab mal im TWI-Praxis rumgewühlt.

Morgen werde ich mal diesen Code ausprobieren:



$regfile = "m8def.dat"
$crystal = 1000000
$baud = 1200
$swstack = 128
$hwstack = 128
$framesize = 128


'Nun eine Temperatur messen:

Twcr = &B10100100 ' Start erzeugen
Gosub Twiloopsub ' Warten bis Start erzeugt

If Status = &H08 Or Status = &H10 Then ' falls start gesendet
Twdr = &B10010000 ' Slave Adresse senden (w)
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H18 Or Status = &H40 Then ' falls ack empfangen
Twdr = &B1010001 ' Befehl zum Messen senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H28 Then
Twcr = &B10010100 ' Stop
' warten bis stop-flag gelöscht:
Do
Loop Until Twcr.twsto = 0

Else
Print "byte nicht gesendet"
Print Status

End If


Else
Print "kein addressack"
Print Status

End if

Else
Print "Startfehler"
Print Status

End If

' nun auslesen:
Twcr = &B10100100 ' Start erzeugen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H08 Or Status = &H10 Then
Twdr = &B10010000 ' Slave Adresse senden (w)
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H18 Then ' falls ack vom slave
Twdr = &B10101010 ' Befehl senden
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H28 Then ' falls ack vom slave
Twcr = &B10100100 ' Erneut Start erzeugen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H08 Or Status = &H10 Then ' falls 2.start gesendet
Twdr = &B10010001 ' Slave Adresse senden (lesen)
Twcr = &B10000100 ' Aktion auslösen
Gosub Twiloopsub ' Warten bis fertig

If Status = &H40 Then ' falls slave addr. gelesen raus
Twcr = &B11000100 ' Ack erzeugen
Gosub Twiloopsub ' warten bis empfangen

If Status = &H58 Or Status = &H50 Then ' dann auslesen
Twidaten1 = Twdr
Else
Print "nichts empfangen 1"
Print Status
End If

Twcr = &B10000100 ' Nack erzeugen
Gosub Twiloopsub

If Status = &H58 Or Status = &H50 Then
Twidaten2 = Twdr
Else
Print "nichts empfangen 2"
Print Status
End If

Twcr = &B10010100 ' Stop




Else
Print "kein lese-slave"
Print Status
End If

Else
Print "kein 2.Start raus"
Print Status
End If

Else
Print "kein command gesendet"
Print Status
End If

Else
Print "kein slaveschreib"
Print Status
End If

Else
Print "kein Start"
Print Status
End If

' Twidaten1/2 enthalten den Temperaturwert.
Print Twidaten1
Print Twidaten2

Twiloopsub:
Do : Loop Until Twcr.twint = 1
Status = Twsr And &HF8
Return



Mal schauen, was sich da so tut...

Murus
31.07.2007, 16:28
Huch, jetzt steh ich aber ganz auf dem Schlauch.

Hab jetzt den obigen Code mit der Statusauswertung getestet. (Habe zusätzlich alle Statusinformationen per UART ausgeben lassen)

Gemäss den Statusbytes funktioniert alles prächtig. Die Kommunikation mit dem Thermometer läuft haargenau nach dem Protokoll ab, genau so, wie es sein muss, die Statusbytes stimmen immer völlig überein.

Nur enthalten die beiden Bytes Twidaten1/2 falsche Informationen... Twidaten1 hat immer den Wert 196 und Twidaten2 hat den Wert 0, obwohl die Kommunikation zum Thermometer funktioniert.

Jetzt kommt natürlich der Verdacht auf, dass das twdr nicht korrekt in die Bascom-Variable übernommen wird... twidaten1/2 sind beide als Byte deklariert...
Zur Erinnerung: die Bascom-Software Routine funktioniert. Da werden alle Werte korrekt übernommen und angezeigt. Gemäss Statusbyte-Auswertung funktioniert auch meine Kommunikation, nur enthalten die beiden Bytes falsche Daten (Twidaten1 und Twidaten2)...

Was kann das sein?

Hier noch die Status Bytes der Kommunikation mit dem Temperatursensor. Zuerst wird der Befehl geschickt, eine Messung zu starten. Danach soll das Thermometer den Wert ausgeben (2 Bytes):

H8 - Start gesendet
H18 - Slaveadresse für schreiben gesendet, Ack empfangen
H28 - Datenbyte gesendet, Ack empfangen (Befehl für Temperaturmessung)

Nun die Temperatur auslesen:
H8 - Start gesendet
H18 - Slaveadresse für Schreiben gesendet, Ack empfangen
H28 - Datenbyte gesendet, Ack empfangen (Befehl zum Temperatur ausgeben)
H10 - Wiederholter Start gesendet (muss so sein)
H40 - Slaveadresse für Lesen gesendet, Ack empfangen
H50 - Datenbyte empfangen, Ack empfangen (Temperaturbyte 1 vom Thermometer, AVR muss Ack ausgeben)
H58 - Datenbyte empfangen, NAck empfangen (Temperaturbyte 2 vom Thermometer, AVR muss NACK ausgeben)

Von daher scheint es ja zu funktionieren... nur enthalten die beiden Temperaturbytes Käse...

What to do?

Gruss
Mario

linux_80
31.07.2007, 22:46
Wenn er 196 zurückliefert hat er noch nix gemessen, scheint als klappt das starten der Messung nicht, dann kommt immer -60°C raus !
Das kommt aber auch raus, wenn man beim ersten mal zu schnell ausliest, man muss dem DS1631 etwas Zeit lassen damit er messen kann, bis zu 750ms bei max. Auflösung (laut DB) !

Wie oft hintereinander hast Du gemessen, lass mal solange die Temperatur auslesen bis ein anderer Wert kommt ?!

Schreib auch mal das 8. Bit in der Zeile "Befehl zum Messen senden" dazu, oder gibs auch Hexadezimal an (&H51), nicht das Bascom das anders umrechnet :-k

Murus
31.07.2007, 22:52
Achjaa.. stimmt... der Sensor braucht ja seine 750ms... heieiei... voll vergessen...

*ausprobier*

Jaaa... es geht :) Das fehlende 8. Bit ist ein Tippfehler, im Code passts.

Suupi, vielen Dank für den Hinweis!!

Herzlichen Gruss und Danke

Mario