PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit RS 485 Bus



habunus
28.11.2007, 20:37
Hallo,

habe mir eine Schaltung aufgebaut mit 2 x Atmega 16, 2 x Max 485 und einem LCD Display, somit will einen RS 485 Bus realisieren.

Der erste Atmega soll als Master dienen und der zweite Atmega als Slave. Diese habe ich auf einem Steckbrett aufgebaut mit folgendem Code:

Master Code:

$regfile = "m16def.dat"
$crystal = 10000000
$baud = 1200
Config Portd.2 = Output
Portd.2 = 1


Dim i As Byte
Dim wTest As Word

I = 1
'wTest = 35000

Do
Portd.2 = 1
Printbin I
Print "1" ; I
'waitms 10
Portd.2 = 0
Loop
End



Slave Code:

$regfile = "m16def.dat"
$crystal = 10000000
$baud = 1200
Config Portd.2 = Input
Portd.2 = 0

Dim I As Byte

' - - - Lcd - - -
Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Porta.0 , Db5 = Porta.1 , Db6 = Porta.2 , Db7 = Porta.3 , E = Porta.5 , Rs = Porta.4 'Ausgänge zum LCD
Config Lcdbus = 4
Config Lcdmode = Port
Config Serialin = Buffered , Size = 20
' - - - - - - - - -

Do
If Usr.rxc = 1 Then ' Wenn Byte empfangen...
I = Udr 'Byte aus UART auslesen
Select Case I
Case "1"
Cls
Lcd "Hallo AVR Nr.1"
Case "2"
Cls
Lcd "Hallo AVR Nr.2"
Case Else
Cls
Lcd "Nichts"

End Select
End If
Loop

Leider bekomme ich auf dem LCD am Slave die Meldung Nichts angezeigt. Die Schaltung habe ich auch schon etliche male kontrolliert.

Würde mich über eine Hilfe freuen.

Mfg

Tobias

digitali
28.11.2007, 21:16
Ich habe das hier mit zwei ATMega128 aufgebaut:

Sender:


$regfile = "m128def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

Config Pinc.6 = Output
Config Pine.2 = Output
Config Graphlcd = 240 * 64 , Dataport = Portd , Controlport = Portc , Ce = 2 , Cd = 3 , Wr = 0 , Rd = 1 , Reset = 4 , Fs = 5 , Mode = 6

Dim A(8) As Byte
Dim B As Byte

Bl Alias Portc.6
Rs485 Alias Porte.2

Bl = 1
Rs485 = 1
Cursor Off
Cls

Locate 1 , 1
Lcd "RS485 Sender"

A(1) = 128 'ID
A(2) = 254 'ID
A(3) = 1 'Absender
A(4) = 2 'Empfaenger

Do
For B = 0 To 255
A(5) = B
A(6) = Crc8(a(1) , 5) 'Checksumme
Locate 7 , 1
Lcd A(6) ; " "
Gosub Send_data
Waitms 250
Next B
Loop

End

Send_data:
Printbin A(1) ; 6
Return



Empfaenger:


$regfile = "m128def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

Config Pinc.6 = Output
Config Pine.2 = Output
Config Graphlcd = 240 * 64 , Dataport = Portd , Controlport = Portc , Ce = 2 , Cd = 3 , Wr = 0 , Rd = 1 , Reset = 4 , Fs = 5 , Mode = 6
Rs485 Alias Porte.2
Enable Interrupts
Enable Urxc
On Urxc Isrlabel

Dim A As Bit
Dim B As Byte
Dim Zeiger As Byte
Dim Db1(6) As Byte
Dim Db2(6) As Byte

Bl Alias Portc.6
Bl = 1
A = 0
Zeiger = 1
Rs485 = 0

Cursor Off
Cls
Locate 1 , 1
Lcd "RS485 Empfaenger"

Do
If A = 1 Then
Locate 3 , 1
Lcd "Absender : " ; Db2(3) ; " "
Locate 4 , 1
Lcd "Empfaenger: " ; Db2(4) ; " "
Locate 5 , 1
Lcd "Datensatz : " ; Db2(5) ; " "
Locate 7 , 1
Lcd "Checksumme: " ; Db2(6) ; " "
A = 0
End If
Loop

End

Isrlabel:
Db1(zeiger) = Udr
If Zeiger = 6 Then
For B = 1 To 6
Db2(b) = Db1(b)
Db1(b) = 0
Next B
Zeiger = 1
A = 1
End If
If Db1(1) = 128 Then
Db2(1) = Db1(1)
Db1(1) = 0
Incr Zeiger
End If
If Db2(1) = 128 And Db1(2) = 254 Then Incr Zeiger
Return




Ich uebertrage in diesem Falle ersteinmal sechs Bytes. Das soll dann spaeter eine Art Protokoll werden. Das ganze laeuft wunderbar stabil. Allerdings habe ich da auch einige Zeit experimentiert bis es einwandfrei lief.
Der Empfaenger sucht zunaechst nach den zwei ID-Bytes. Wenn diese korrekt angekommen sind, dann werden die 4 nachfolgenden Bytes verarbeitet. Sobald die Uebertragung vollstaendig ist, werden die Daten auf dem LC-Display angezeigt.
Gerade heute habe ich noch ein paar MAX485 bekommen. Sobald ich den naechsten Bastelflash bekomme, werde ich einen dritten AVR mit Display in den Bus haengen und die Software so umschreiben, dass jeder Senden und Empfangen kann.
Dafuer hatte ich auch das Empfaenger- und Senderbyte vorgesehen.

Ein Foto von dem Drahtigel:
http://www.elektronik-web.de/fotos/rs485.jpg

Mit freundlichen Gruessen
Digitali

habunus
28.11.2007, 21:41
Hallo Digitali,

werde mich mal in deinen Code einlesen bin zwar noch nicht so fit mit der Programmierung versuche es aber mal ;-)

Mfg

Tobias

digitali
28.11.2007, 22:04
Ich habe das mal auf das Notwendigste abgespeckt:

Sender:


$regfile = "m128def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

Config Pinc.6 = Output
Config Pine.2 = Output
Config Graphlcd = 240 * 64 , Dataport = Portd , Controlport = Portc , Ce = 2 , Cd = 3 , Wr = 0 , Rd = 1 , Reset = 4 , Fs = 5 , Mode = 6

Dim A As Byte

Bl Alias Portc.6
Rs485 Alias Porte.2
Bl = 1

Rs485 = 1 'Senden

Cursor Off
Cls
Locate 1 , 1
Lcd "RS485 Sender"

A = 128

Do
Printbin A
Waitms 500
Loop

End


Empfaenger:


$regfile = "m128def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

Config Pinc.6 = Output
Config Pine.2 = Output
Config Graphlcd = 240 * 64 , Dataport = Portd , Controlport = Portc , Ce = 2 , Cd = 3 , Wr = 0 , Rd = 1 , Reset = 4 , Fs = 5 , Mode = 6
Rs485 Alias Porte.2
Enable Interrupts
Enable Urxc
On Urxc Isrlabel

Dim A As Bit , Db As Byte

Bl Alias Portc.6 ' Hintergrundbeleuchtung LC-Display
Bl = 1
A = 0

Rs485 = 0 'Empfangen

Cursor Off
Cls
Locate 1 , 1
Lcd "RS485 Empfaenger"

Do
If A = 1 Then
Locate 4 , 1
Lcd Db
A = 0
End If
Loop

End

Isrlabel:
Db = Udr
A = 1
Return


Es wird nur ein einziges Byte (128) uebertragen. Sobald der UART ein Zeichen empfaengt wird ein Interrupt ausgeloest. In der Urxc-Isr wird dann das Byte ausgelesen (Db=UDR). Und das war's dann auch schon. Eigentlich total simpel.

Mit freundlichen Gruessen
Digitali

habunus
28.11.2007, 22:06
Hallo Digitali,

also habe mal den Code etwas angepasst für den Atmega 16 kann sein das ich da noch einen fehler habe.

Master
$regfile = "m16def.dat"
$crystal = 1000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

Dim A(8) As Byte
Dim B As Byte

Bl Alias Portd.1 'txd vom atmel verbunden mit max485 pin 4 (DI)
Rs485 Alias Portd.2 'INT0 vom atmel verbunden mit max485 pin 2,3 (RE,DE)

Bl = 1
Rs485 = 1

A(1) = 128 'ID
A(2) = 254 'ID
A(3) = 1 'Absender
A(4) = 2 'Empfaenger

Do
For B = 0 To 255
A(5) = B
A(6) = Crc8(a(1) , 5) 'Checksumme
Gosub Send_data
Waitms 250
Next B
Loop

End

Send_data:
Printbin A(1) ; 6
Return


Slave:
$regfile = "m16def.dat"
$crystal = 1000000
$hwstack = 32
$swstack = 20
$framesize = 40
$baud = 9600

' - - - Lcd - - -
Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Porta.0 , Db5 = Porta.1 , Db6 = Porta.2 , Db7 = Porta.3 , E = Porta.5 , Rs = Porta.4 'Ausgänge zum LCD
Config Lcdbus = 4
Config Lcdmode = Port
' - - - - - - - - -

Rs485 Alias Portd.2 'INT0 vom atmel verbunden mit max485 pin 2,3 (RE,DE)
Enable Interrupts
Enable Urxc
On Urxc Isrlabel

Dim A As Bit
Dim B As Byte
Dim Zeiger As Byte
Dim Db1(6) As Byte
Dim Db2(6) As Byte

Bl Alias Portd.0 'RXD vom Atmel zu Max 486 Pin 1 RO
Bl = 1
A = 0
Zeiger = 1
Rs485 = 0

Cursor Off
Cls
Locate 1 , 1
Lcd "RS485 Empfaenger"

Do
If A = 1 Then
Locate 2 , 1
Lcd "Absender : " ; Db2(3) ; " "

Locate 2 , 1
Lcd "Empfaenger: " ; Db2(4) ; " "

Locate 2 , 1
Lcd "Datensatz : " ; Db2(5) ; " "

Locate 2 , 1
Lcd "Checksumme: " ; Db2(6) ; " "
A = 0
End If
Loop

End

Isrlabel:
Db1(zeiger) = Udr
If Zeiger = 6 Then
For B = 1 To 6
Db2(b) = Db1(b)
Db1(b) = 0
Next B
Zeiger = 1
A = 1
End If
If Db1(1) = 128 Then
Db2(1) = Db1(1)
Db1(1) = 0
Incr Zeiger
End If
If Db2(1) = 128 And Db1(2) = 254 Then Incr Zeiger
Return

Bekomme keine Daten vom Master.

digitali
28.11.2007, 22:13
Bl Alias Portd.1 'txd vom atmel verbunden mit max485 pin 4 (DI)
Bl Alias Portd.0 'RXD vom Atmel zu Max 486 Pin 1 RO

Ich hatte mein Listing schlecht dokumentiert. Bl steht bei mir fuer Backlight. Damit schalte ich lediglich die Hintergrundbeleuchtung des Displays ein. Das musst Du bei Dir wieder loeschen.

Mit freundlichen Gruessen
Digitali

habunus
28.11.2007, 22:18
Also brauche ich nur beim Master den Portd.2 ?

Mfg

Tobias

digitali
28.11.2007, 22:43
Pin 2 und 3 vom MAX 485 sind ja zusammengeschaltet. Dort wird zwischen Senden und Empfangen umgeschaltet. Das mache ich beim Sender und Empfaenger jeweils mit dem PortPin E.2.
RxD vom AVR an Pin1 (RO) vom MAX und TxD vom AVR an Pin4 (DI) vom MAX.

Mit freundlichen Gruessen
Digitali

Vitis
29.11.2007, 17:29
jaja, die 485 ... ich hatte mal probleme damit, weil die
A-undB-Leitung floatete. hab sie dann damit gelöst, dass ich
Pullup-Pulldown und Abschlusswiderstände einlötete.
Dann gings auf Anhieb.
ach so, eins noch, warum verwendest Du nicht einfach den
PCA82C250? Ist zwar n CAN-Bus Baustein, die Funktion
ist aber vergleichbar, bis auf den Punkt, dass man sich
die Umschaltung der Datenflussrichtung schenken kann.

habunus
01.12.2007, 14:43
Hallo Vitis,

habe die Abschlusswiderstände an den Bus gemacht und siehe da es funzt :-)

Danke

Mfg

Tobias

Vitis
02.12.2007, 01:59
jaja, kleine Ursache ....

Accenter
13.12.2007, 01:37
Ich habe mir jetzt den reduzierten Code angesehen und versucht zu verstehen was da genau vorsich geht. Arbeite zum ersten mal mit RS-485.

Ich würde den hier gezeigten Code gerne so modifizieren, dass 3 verschiedene Bits übertragen werden. Ich habe einen Sender und einen Empfänger und möchte am Empfänger 3 Relais schalten.

Also z.B. Bit A,B,C
Jedes steht für den Zustand von einem Relais.

Ich muß dam im Sende und Empfangsprogramm A,B und C als Bit dimensionieren. Richtig?

Mit Printbin A sende ich das Bit A dann vom Sender zum Empfänger.

und wie empfange ich es dann?

A = UDR?

Falls jemand einige Zeilen hat damit ich das verstehe wäre ich sehr dankbar.

Vitis
13.12.2007, 09:20
das kommt immer drauf an, was du genau möchtest.
ist es nur ne simplexverbindung und es macht nix
wenn auch mal n falsches relais anzieht
gehts am einfachsten.
senden mit udr=zustaende,
empfangen mit zustaende=udr

ist aber was "wichtiges" dran, sodass fehlschaltungen
unbedingt unterbunden sein müssen brauchts ne
prüfsumme um die korrekte übermittlung zu checken.
nur, woher weiß der controller, das das übertragene
byte kein datenbyte ist, sondern ne prüfsumme
und auch so behandelt werden muss ...
es gibt auch da mehrere wege.
zum einen kann man auch mit der 9-bit-uart arbeiten
und das neunte bit als marker verwenden
oder man bastelt sich n protokoll, das definierte
startzeichen, endzeichen und/oder definierte paketlängen
hat.
soll der bus um zusätzliche mitglieder erweitert werden
brauchts noch mindestens ne empfängeradresse.
dann hätten wir schon mindestens 2 bytes zu übertragen,
so in etwa 1.byte mit 9 bit, paketstart, zweites byte empfänger.
sinnigerweise währen aber 3 byte, also start, empfänger, anweisung.
das währ dann bei 8-bit-uart. bei 9-bit kann man sich das erste byte
wieder schenken, weil da das 9. bit als startzeichen herhalten kann,
als erstes zeichen 8-bit empfänger, neuntes bit ist paketstart.
zweites zeichen, ist dann 8-bit, ist dann die anweisung.

bei wichtiger fehlerlosigkeit käme dann noch die entsprechende
prüfsumme als byte mit hinten dran.
das kann man dann noch ausbauen mit zeichenzähler für datenpakete
unterschiedlicher länge, mit endzeichen usw. usw.

ein recht komplexes protokoll findest du z.b.
unter https://www.roboternetz.de/phpBB2/files/decodierungstabellev12.pdf
beschrieben

es sind dir da schon viele möglichkeiten gegeben, was du brauchst
weißt nur du alleine.

Accenter
13.12.2007, 16:26
Kann man die Sicherheit auch erhöhen wenn jedes Bit z.B. 5 mal hintereinander empfangen werden muß damit das richtige Relais anspricht? Die Geschwindigkeit ist in meinem Fall total unkritisch. Selbst wenn es 500msec dauert bis am Empfänger etwas passiert wäre das egal.

Das mit UDR verstehe ich leider total nicht :-(
Hat jemand für mich einige Zeilen Code damit ich verstehe was da passiert?

Danke für eure Hilfe.

Vitis
13.12.2007, 22:45
also,

das UDR ist nur n Register, ne Speicherzelle könnte man sagen,
die genau ein Byte fasst.
Bevor man es verwendet muss man aber erst die
UART, also die serielle Schnittstelle konfigurieren.
Üblicherweise ist die Konfiguration
z.B. 19200,8,n,1
19200 ist die Baudrate, also die Übertragungsgeschwindigkeit
8 ist die Paketlänge, also 8 BIT, nicht Byte, also 1 Byte
n steht für "no parity" also kein Paritätsbit
1 steht für ein Stopbit.

ist die UART konfiguriert stellt sie verschiedene Flags
Interrupts und Register bereit, die man verwenden
kann.
Da gibts z.B. das RXC also das receive complete Flag,
es zeigt an, dass ein Zeichen ganz empfangen wurde.
Es kann auch ein entsprechender Interrupt ausgelöst
werden, das währe dann der URXC-Interrupt.
Äquivalent gibts das auch zum Senden,
da nennt sich das dann TXC, also Sendeübertragung complete-Flag
und UTXC also Zeichen gesendet Interrupt.

Wird nun ein Zeichen empfangen wird dies in der
UDR-Speicherstelle abgelegt und kann dort dann ausgelesen
werden. Die Anweisung währe dann:
beliebige_variable = UDR
Zum Senden schreibt man einfach ein Zeichen, also ein
Byte in das gleiche Register, also:
UDR=beliebiges_byte

das wars auch schon.