PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I2C - ein Buch mit 7 Siegeln :( [verbinden 2er Controller]



daniel.weber
18.03.2008, 23:23
Hallo zusammen,
nach einigem hin und her habe ich mich nun doch entschlossen I²C zum verbinden meiner Controller zu verwenden. Ich schildere einfach nochmal kurz was genau ich vor habe.

Derzeit besteht die Schaltung aus 2 Controllern:

1 x Mega32 - soll der Master sein, Sensorenwerte kommen hier rein und werden in "Bewegung" umgesetzt.

1 x Mega8 - Dient zur Motorsteuerung, demnach müsste das ja der Slave sein.

Der Mega32 soll dem Mega8 nun bestimmt Befehle senden z.B. "A1", der Mega8 weis nun was er zu tun hat z.B. vorwärts fahren.
---

Nun schreib ich einfach mal eben auf, was ich bereits über I²C erfahren habe und hoffe, dass das der Richtigkeit entspricht.

Ich weis bereits, dass es immer einen Master geben muss und mehrere Slaves geben kann, jede I²C Komponente hat eine bestimmte Adresse, mit der sie eindeutig angesprochen werden kann.

Zum Schaltungsaufbau:
Ich muss ja 2 Ports pro Controller zur Verfügung stellen für SDA und SCL, diese werden dann mit den Komponenten verbunden und über einen 1K Widerstand auf VCC gezogen? (sollte ich hier mist erzählen, bitte korrigieren :) )

---
Kommen wir nun zu meiner eigentlichen Frage, wie zum Teufel programmiert man das nun In Bascom, wie sage ich welchem Controller wer Master und wer Slave ist, wie kann ich z.B. einen String senden und den beim anderen wieder empfangen?

Ich weis, dass sich eine I²C Routine folgendermaßen aufbaut:



i2c_init
i2c_start
i2c_sendebyte(slaveid)
i2c_sendebyte(wert1)
i2c_sendebyte(wert2)
i2c_stop

aber ich habe kA wie man das jetzt als sinnvolles Programm zusammen setzt.

Bin für jede Art von Hilfe sehr dankbar, es gibt ja auch zu diesem Thema nicht wirklich gute Tutorials, habe zumindest keine gefunden, bis auf das hier im RN Wissen.

LG
Daniel

linux_80
19.03.2008, 03:00
Hallo,

der Teil zum Master ist in Bascom schon enthalten, gibt auch schon genug Beispiele überall dazu.
Nur für I2C-Slave ist nichts enthalten, deshalb die etwas komplizierte Wiki-Seite um das selber hinzubekommen. Man könnte sich auch die Lib bei MCS kaufen um den Slave mit Basocm-internen Mitteln hinzubekommen, damit hab ich aber keine Erfahrung ;-)

Um mehrere Zeichen zu übertragen, musst Du dir noch eine Art Protokoll überlegen, abgesehen vom Start/Stop usw. damit der Slave weiss was auf ihn zu kommt. Als Beispiel könnte man die Beschreibungen der Boards für Motorsteuerungen hernehmen. Bei diesen ist die Anzahl der Bytes pro Übertragung meist festgelegt, und jedes Byte hat eine feste funktion, also Befehle oder Werte.
Schau einfach mal in die Beschreibung vom RN-Motorcontrol, ist zumindest ein Ansatz.

StevieL
19.03.2008, 19:44
Hallo Daniel,

du solltest (zumindest beim Slave) auf jeden Fall die Pins für SCL und SDA verwenden, die hardwaremäßig dafür vorgesehen sind.

Folgender Code für einen Slave sollte funktionieren. Es wird z. B. der Buchstabe S oder X übertragen, gefolgt von einem weiteren Wert:



$regfile = "m8def.dat"
$crystal = 8000000

'Subs deklarieren
Declare Sub Twi_init_slave

'SDA = pind.4 ; SCL = pind.5 (Hardware TWI)

Waitms 100
Call Twi_init_slave ' TWI aktivieren

Const Maxanzahlbyte = 2 ' Wenn mehr Zeichen kommen werden diese verworfen !
Dim Messagebuf(maxanzahlbyte) As Byte
Dim Anzahlbuf As Byte ' Anzahl Zeichen die gesendet wurden
Dim Neuemsg As Byte ' zeigt an wenn eine neue Message gültig ist

Dim Twi_control As Byte ' Controlregister lokale kopie
Dim Twi_status As Byte
Dim Twi_data As Byte
dim tmp as byte
Const Slaveid = &H82 'Adresse für I2C


Twi_data = 0
Anzahlbuf = 0
Neuemsg = 0

Do

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

If Twi_control = &H80 Then
Twi_status = Twsr And &HF8 ' Status ->Bit3 bis Bit7

Select Case Twi_status

' Slave Adress received, wir sind gemeint !
Case &H60 :
Twcr = &B11000100 ' TWINT löschen, erzeugt ACK
Anzahlbuf = 0
Neuemsg = 0 ' Flag für Message ungültig

' Byte mit ACK
Case &H80 :
If Anzahlbuf < Maxanzahlbyte Then
Incr Anzahlbuf ' zähler +1
Messagebuf(anzahlbuf) = Twdr
End If
Twcr = &B11000100 ' TWINT löschen, erzeugt ACK

' Stop oder restart empfangen
Case &HA0 :
Twcr = &B11000100 ' TWINT löschen, erzeugt ACK
' es müssen 2 Byte sein, damit das Paket OK ist
If Anzahlbuf = 2 Then
Neuemsg = 1 ' Flag für Message gültig
Else
Neuemsg = 0 ' Flag für Message ungültig
End If
'Master will ein Byte haben
Case &HA8:
Twdr = Vers
Twcr = &B11000100
'Master will weiteres Byte haben
Case &HB8:
'TWDR = Count
'incr Count
'Twcr = &B11000100
' letztes Byte mit NACK, brauchen wir nicht
Case &H88 :
Case &HF8 :
' Fehler, dann reset TWI
Case &H00 :
Twcr = &B11010100 ' TWINT löschen, reset TWI
' was anderes empfangen, sollte nicht vorkommen
Case Else :
Twcr = &B11000100 ' TWINT löschen, erzeugt ACK

End Select

End If

' ein gültiges Paket angekommen
If Neuemsg = 1 Then
Neuemsg = 0 ' Flag wieder löschen
Select Case Messagebuf(1)
Case "S":
Tmp = Messagebuf(2)
'Mache etwas damit

Case "X":
Tmp = Messagebuf(2)
'Mache etwas anderes
Case Else:
End Select
Waitms 100
End If

Waitms 10

Loop


End


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

StevieL
19.03.2008, 19:50
Mit dem Master sendest du einfach:



I2cstart
I2cwbyte Slaveadr
I2cwbyte &H58 '58 = X
I2cwbyte Wert1
I2cstop
'oder
I2cstart
I2cwbyte Slaveadr
I2cwbyte &H53 '53 = S
I2cwbyte Wert2
I2cstop


SlaveAdr und SlaveId im Code für den Slave müssen natürlich identisch sein. Du kannst da eine beliebige Adresse verwenden. Lediglich bei den I2C-Bausteinen (PCF, PCA u. s. w) sind die Adressen vorgegeben.

daniel.weber
19.03.2008, 23:42
Hallo StevieL,
vielen Dank!! Dein Beitrag hat mir sehr geholfen, hab es damit hinbekommen und muss sagen, wenn man erstmal durch I2C durchgeblickt hat, ist es eine echt tolle Sache :-k

Ein Problem habe ich allerdings noch:

Ich lese die UART Eingabe von einem Konsolen Programm mit der Funktion inkey() aus, da erhalte ich die Eingabe, also die Buchstaben im ASCII Format, soweit ich weis. Wie kann ich diese Eingabe nun kompatibel zum senden mit I2C machen, da brauche ich das im ? Hex Format ?

Gibt es da eine Funktion die mir das umrechnet? oder wie könnte man das lösen?

Nochmal vielen Dank für deine Hilfe! :cheesy:

LG
Daniel

"Update"
Sehe grade die Funktion inkey() gibt nur den ASCII Wert an.
z.B.: X = 88, S = 83
hmm :-k

StevieL
20.03.2008, 06:20
Hallo Daniel,

ob du das als Hexwert, Binärwert oder sonstwie hast, spielt ja keine Rolle. Dein Slave muss nur wissen, auf welchen Wert er wie reagieren muss. Der Code, den ich gepostet habe, ist ein Programm von mir, da habe ich nur ein paar Sachen rausgelöscht. Vollständig ist das:

Select Case Messagebuf(1)
Case "S":
Tmp = Messagebuf(2)
If Tmp <> Portstate Then 'liegt der Wert so schon an?
Portd = Tmp
Portstate = Tmp
End If
Case "X":
Tmp = Lookup(messagebuf(2) , Prozente) 'Werte aus Tabelle holen
Pwm1a = Tmp
Pwm1b = Tmp
Case Else:
End Select
In diesem Teil werden ja die beiden gesendeten Bytes ausgewertet. Abhängig vom ersten Byte wird der Wert des zweiten Bytes umgesetzt. Ob man da jetzt Case "S" schreibt oder Case 88 ist Wurst. Mein Beispiel verwende ich für eine Lampensteuerung, die in meine Aquarienbeleuchtung eingebaut ist. Wenn "S" gesendet wird, wird das nächste Byte ausgewertet und auf den Port D gelegt. Hiermit werden die Vorschaltgeräte mehrerer Leuchtstoffröhren ein- oder ausgeschaltet. Wird ein "X" gesendet und danach ein Wert zwischen 0 und 100 (=Prozent) werden zwei Vorschaltgeräte über PWM angesteuert und sorgen für die Dimmung.

Edit: Das Grundgerüst des Programm stammt natürlich aus der RN-Wiki!

daniel.weber
20.03.2008, 12:29
Ok danke, dann habe ich es verstanden, das mit dem zweiten Byte hatte ich in meinem Programm auch schon so gemacht.

1. Byte sagt dem Mega8 worum es eigentlich geht, z.B. Geschwindigkeit und das 2. Byte gibt den Wert der Geschwindigkeit an.

Nochmal vielen Dank!

LG
Daniel

daniel.weber
22.03.2008, 00:24
Hallo habe doch nochmal eine Frage zum Thema.

Bin jetzt auf folgendes Problem gestoßen, bei den I2C Modulen (nehmen wir zum Beispiel mal das Kompassmodul CMPS03) sende ich dem Modul (da ist ja auch ein MC auf dem Board) ein bestimmtes Byte und bekomme dann den entsprechenden Wert zurück (in diesem Beispiel die Himmelsrichtung) wie lässt sich so etwas nun wieder realisieren, ich dachte bei der ganzen Sache wird nur eine Übertragung zum Slave durchgeführt, wie aber kommen nun die Daten aus dem Slave zum Master?

Wenn ich meinem Motorsteuerungsboard z.B. jetzt eine Firmware Version gebe und ich möchte die per I2C auslesen, wie müsste ich da vorgehen? Muss ich den Master da dann auch als "Slave" programmieren, so dass der auch empfangen kann und den Slave als "Master"... ich bin mal wieder sehr verwirrt :( - schwieriges Thema, dieses I2C :(

Bin wieder für Tipps sehr dankbar.

LG und frohe Ostern
Daniel Weber

StevieL
22.03.2008, 15:29
Hi,

schau dir doch mal in der Bascom-Hilfe die I2C-Befehle an. Da wirst du auch Befehle zum Lesen finden.

Dann schaust du mal im Code des Slaves bei


'Master will ein Byte haben
Case &HA8:
Twdr = Vers
Twcr = &B11000100
'Master will weiteres Byte haben
Case &HB8:
TWDR = Count
incr Count
Twcr = &B11000100

Vers ist bei mir eine Konstante, die die aktuelle Firmwareversion beinhaltet.

daniel.weber
22.03.2008, 17:20
Hallo,
vielen Dank für deine Antwort, habe mir das in der Hilfe nochmal angeschaut und festgestellt, dass ich das "r" in I2crbyte total überlesen habe :(

Naja habe dann mal versucht anhand der Beispiele aus dem RN Wissen und der Bascom Hilfe einen Code auf die Beine zu stellen, leider liest der Master bei mir immer 255 aus.

Habe das so versucht:

Master:


Function Mc_firmware() As Byte
Local Firmware As Byte
Local Slaveid As Byte
Local Slaveid_read As Byte
Slaveid = &H82
Slaveid_read = &HA8


I2cstart
I2cwbyte Slaveid 'slave adsress
I2cwbyte 0 'aus dem Beispiel
I2cstart 'repeated start
I2cwbyte Slaveid_read 'slave address (read) weis nicht genau was da hinkam, hatte gedacht '&HA8'
I2crbyte Firmware , Nack 'read byte
I2cstop




Mc_firmware = Firmware


End Function


das ganze versuche ich dann mit Print "Aktueller Version" ; Mc_firmware()
auszulesen, bekomme aber wie gesagt immer den Wert 255.

Bin ich überhaupt auf dem richtigen Weg?? wie kommt man auf die slaveid_read?

StevieL
22.03.2008, 19:13
Hi,

wieso denn so umständlich?


Function Mc_firmware() As Byte
I2creceive &H82 , Mc_firmware
End Function

daniel.weber
22.03.2008, 19:32
oh ja hmm das ging jetzt echt einfach... *g* vielen Dank :)
habe da allerdings noch ein Problem und dann hast du mich sowas von glücklich gemacht *g*

Ich weise der Var mc_firmware den Wert "1.02" zu mit:



const mc_firmware = "1.02"


naja aber das mag Bascom aus irgendwelchen Gründen nicht, bekomme den Fehler: "Error 64 - expected".

Wenn ich die Var als integer deklariere dann geht alles, aber dann kann ich nur ganze Zahlen senden.

*verwirrt ist*

LG
Daniel

StevieL
23.03.2008, 10:29
Hallo Daniel,

das ist somit ja als String definiert. Den müsstest du zerlegen und die Bytes einzeln schicken.
Ich würde das einfach als

const mc_firmware = 102
und somit als Byte definieren und später zur Anzeige den Punkt reinsetzen.

Ich mache das bei meinen Anwendungen (Master) so:



Dim Lcdstring As String * 5
Dim Lcdarray(6) As Byte At Lcdstring Overlay

Sub Showversion
Lcd " SL-AQUARISTIK"
Locate 2 , 1
Lcdstring = Str(vers)
Lcdarray(5) = Lcdarray(3) : Lcdarray(3) = Lcdarray(2)
Lcdarray(2) = "." : Lcdarray(4) = "."
Lcd "Pegel V." ; Lcdstring
Wait 2
End Sub

Wenn du das Bytearray mittels Overlay auf den String legst, kannst du die einzelnen Zeichen verschieben wie du willst.
Die Variable Vers wird dann auf dem LCD z. B. als 1.0.2 angezeigt.

StevieL
23.03.2008, 10:36
Hi,

in deinem Fall könntest du das so schreiben:



Dim Lcdstring As String * 4
Dim Lcdarray(5) As Byte At Lcdstring Overlay

Sub Showversion
Lcd " Mc_Firmware"
Locate 2 , 1
Lcdstring = Str(vers)
Lcdarray(4) = Lcdarray(3) : Lcdarray(3) = Lcdarray(2)
Lcdarray(2) = "."
Lcd "Vers. " ; Lcdstring
Wait 2
End Sub