Im Beitrag Beitrag "Modbus RTU Slave Protolollstack"
ging es um die Implementierung von Modbus auf einem AVR, ich hatte da
angeboten meinen Code zu posten, wenn sich jemand findet der diesen Code
in eine representable Form bringt.
Es kamen immer wieder Anfragen per PM somit mache ich einen neuen Thread
auf und poste mal den RTU Slave Teil meiner Implementation.
Dieser Code basiert auf der UART Implementation von P.Fleury, mit
einigen Anpassungen um die Lib modbus-faehig zu machen.
Der Modbus code ist auch irgenwo abgeschrieben, ich erinnere mich an
zwei quellen, eine davon war freemodbus.
Freemodbus war mir aber zu gross fuer einen uC somit hab ich Ihn auf das
notwendigste zusammengekuerzt.
Im aktuellen Code sind auch einige Modbus Funktionen auskommentiert,
zBsp. READ_INPUT_STATUS, das ist aber ziehmlich das selbe wie
READ_COIL_STATUS nur mit anderem Address-Bereich
FORCE_MULTIPLE_COILS
PRESET_MULTIPLE_REGISTERS
Hab ich auch nicht fertig implementiert da es in meinen Anwendungen nie
gebraucht wurde und ich auch keine Modbus-Geraete habe die danach
gefragt haben.
Also dann probiert mal ob Ihr mit dem Code was anfangen koennt.
Fragen an mich hier im Forum, nicht per PM, das gleiche gilt fuer
Verbesserungen, somit haben alle was davon.
Ju
Nochmal an Alle, Fragen bitte hier im Thread posten, nicht per PM.
PM's kann nur ich lesen, die Posts jeder den es interessiert.
Die Frage nach den Bus Treibern.
Obiger code basiert auf der UART Lib von P.Fleury.
Peters Lib wurde insoweit angepasst, das sie mit RS232 und mit RS485
funkioniert. Es muss nur die entsprechende init routine im main
aufgerufen werden.
Bei Verwendung von RS485 ist ein zusaetzlicher Pin am AVR zum senden
notwendig. Als Bustreiber sollte alles gehen was dem entsprechenden
Standard unterstuetzt.
ju
Hi,
leider mangelt es mir da noch an Verständnis....
1. Wie kann man z.B. einem bestimmten Holding einen Wert zuweisen?
2. Wie kann ich einen bestimmten Holding auslesen?
Ist die Datei BSP_Modbus ein Beispiel?
Was soll hier passieren?
Gruß Flo
> Ist die Datei BSP_Modbus ein Beispiel?
Ja, das ist ein Slave er bekommt seine Adresse
aus der Datei JG_Modbus.h
#define SLAVE_ID 11
in diesem Beispiel
Die Holdingregister sind in dem Array
int16_t actProfileData[MAX_WordAddress];
und die Coil Register in
int8_t systemBits[MaxActionBytes];
untergebracht, beide in der BSP_Modbus_main.c deklariert und in der
JG_Modbus.c referenziert.
Da kannst Du entsprechend Werte zuweisen.
Der Slave ist in einer Endlosschleife.
Wenn ueber den UART eine Anfrage vom Master kommt und diese vom Slave
als ordentliches Modbus Datagramm erkannt wird, wird diese Anfrage vom
Slave beantwortet.
Die Validation ist ein Zusammenspiel von UART RX ISR und Timer2 um die
Timeouts und Pausen zu erkennen, danach wird die Checksum und der Inhalt
der Daten von den eigentlichen Modbus Funktionen ueberprueft und
beantwortet.
Der Slave lauft soweit stumm und beantwortet die Anfragen die vom Master
kommen.
Wenn ein Master ein Holdingregister schreibt, dann findet sich das
Resultat an der entsprechenden Stelle im Array
actProfileData[WordAddress]
Wenn der Master ein Holdingregister liest, bekommt er den Wert der an
actProfileData[WordAddress] steht.
Das gleiche gilt fuer die Coil Register respektive das Array
systemBits[MaxActionBytes]
hier wird jede Coil-Register Adresse durch ein Bit representiert.
Ein Fallstrick ist hier, das die meisten Modbus Master bei 1 anfangen zu
zaehlen und die entsprechende Array Position dann Adresse-1 ist.
>> 1. Wie kann man z.B. einem bestimmten Holding einen Wert zuweisen?
ist somit glaube ich geklaert
> 2. Wie kann ich einen bestimmten Holding auslesen?>
Du kannst das nicht, das kann nur ein Modbus Master der ein
entsprechendes Datagramm generiert. Da gibts einige Programme die sogar
frei sind, frag mich aber nicht welche das sind.
Wenn Du HTerm verwendest und Deinen AVR ueber den UART an Deinen PC
haengst, kannst Du Datagramme in HEX eintippen und sie dem AVR senden,
er wird Dir antworten.
> Ist die Datei BSP_Modbus ein Beispiel?> Was soll hier passieren?>
Ist glaub ich auch weiter oben schon beantwortet.
Ju
Hallo,
habe nun mal folgendes gemacht:
Diese Defines für den Atmega16 angepasst:
#define RS485_CPort DDRD
#define RS485_Port PORTD
#define RS485_Pin PIND
#define RS485_Send PD1
#define RS485_PSend PIN1
Ansonsten habe ich nix verändert. Nun versuche ich über Modbus Poll auf
den Slvae Atmega16 zuzugreifen. Getaktet wird der Controller mit 8Mhz
mit dem internen Quarz.
In Poll eingestellt:
Baudrate: 9600
8 Datenbits
Parität: keine
1 Stopbit
Modus: RTU
Mein USB Com Adapter blinkt bei TX, sprich da wird was gesendet nur auf
dem Board tut sich nix.
Als Polldefinition habe ich Holding Funktion 3 angegeben von 3 - 11
lesen und die Slave ID 11.
Als Fehler kommt Timeout.
Gruß Florian
Modbus RTU ist zeitabhaengig, die Pausen muessen exakt eingehalten
werden, sonst wird das Paket nicht als gueltig erkannt.
Die Timer in meinem Beispiel sind glaube ich fuer einen 16MHz Crystal
konfiguriert. Da also mal als erstes ansetzen und deinen Timer so
konfigurieren das er der Zeit von einem Bit Deiner serial Uebertragung
entspricht.
Mit dem internen RC vom Atmel und einem zeitkritischen Serial Protokoll
zu arbeiten ist auch keine glueckliche Wahl.
Klemm an Deinen AVR mal einen 16MHz Crystal und dessen C's dran, dann
sollte es mit dem Beispiel code klappen.
Ist glaube ich fuer den Einstieg das beste, spaeter wenn Du ein paar
Erfolgserlebnisse hast, kannst Du immer noch mit dem RC vom AVR
experimentieren.
Ju
Hab jetzt mal den 16Mhz Quarz in Bewegung gesetzt.
Allerdings bekomme ich nur Timeout.....
Hab an deinem Code nur auf PD1 geändert, da dies am Atmega16 der TxD Pin
ist....
Ansonsten ist alles unverändert... mal schauen ob es an der Hardware
liegt.
da verwende ich die hier:
http://www.sedu-board.de/
Gruß Flo
flo schrieb:> Mit was für einem Controller hast du bei deinem Beispiel gearbeitet?>> Gruß Flo
Die Entwicklung war auf einem m644, weil der 2 UARTs hat, der BSP Code
lauft auf einem m32, sollte also Pin kompatibel zu Deinem m16 sein.
Zu dem von Dir geaenderten Pin.
Ich weiss nicht was Du da wo geaendert hast, es ist aber normalerweise
keine Aenderung noetig wenn Du mit RS232 an den PC gehst.
Die Definitionen
RS485_CPort
RS485_Port
RS485_Send
und deren Verwendung sind nur da wenn Du mit RS485 arbeitest, da gibt es
einen Send_Enable Pin am Bus Treiber der vor dem senden auf High gesetzt
werden muss.
RS232 hat das nicht.
Wenn Du obige definition auf deinen TX pin geaendert hast, dann kann das
nicht funktionieren.
Ju
Hi,
ich nutze natürlich den RS485 Mode der wird doch auch im Beispiel
genutzt.
Der RTS mit dem wird die Richtung normalerweise beim Modbus dann
bestimmt.
Diesen hab ich auch auf einen Pin gelegt.
Zunächst hatte ich hier noch nen Denkfehler drin.
Aber der von dir angesprochene RS485_CPort liegt auf PD7 bei mir.
Das scheint auch so zu passen.
Nur bekomm ich leider nur Timeout.....
Ich werd jetzt mal das Beispiel von freemodbus org testen.
Gruß Flo
Hi Ju,
irgendwie komme ich da nicht weiter:
Hardware - Sedu Board:
Atmega16
Ich habe einen USB zu RS485 Wandler am PC dran.
In deinem Beispiel steht auch, dass das Beispiel für RS485 ist.
Der CPort ist bei mir jetzt PD6 hab das nochmal durchgemessen, das passt
jetzt eigentlich so. Bei dir war dieser Pin auf PD5 was ja eigentlich
egal ist und halt nur wegen der Beschaltung ist.
Als Antwort bekomme ich immer Read error : break --> Write error :
break.
Vielleicht liegt es auch an den Einstellungen in Modbus Poll?
Auch an der Slave ID oder Baudrate habe ich nix geaendert.
Kannst du mir vielleicht mal eine funktionierende Version kompilieren,
die mit dem Sedu Board laufen sollte
Gruss Florian
@flo
Ich kenne das SEDU board nicht und habe auf der o.g. Seite auf die
Schnelle keinen Schaltplan gefunden.
Kannst Du mir den Link zum Schaltplan posten.
Auf der Seite steht was von einem FT232, von einem Bus Treiber und
Jumpern.
Vielleicht solltest Du mal direkt an Deinem uC messen ob da was ankommt
und das das was der uC sendet auch bis zu Deinem PC durchkommt.
Ist die Baudrate richtig eingestellt?
ju
Zum Besseren Verstaendniss der Definitionen in meinem Beispiel
RS485_CPort => Das DDR Register
RS485_Port => Der Port
RS485_Send => der Ausgangs Pin des PORT
Ju
Ich nutze nicht den FT232 der auf dem Sedu Board ist!
Folgende Topologie:
PC mit USB---> Wandler auf RS485 ---> SN75176 --> RX-TX am Atemga16
Auf dem Sedu Board wird der Jumper auf Software gesetzt und die Jumper
für Tx und Rx werden gesetzt.
An PD6 kann die Richtung wenn auf Software gejumpert ist geändert
werden.
Leider kann ich in Modbus Poll einstellen was ich will ich bekomme
ständig Timeout oder Read und Write Error.
Habe die Belegung am Stecker mehrmals überprüft, dass sollte passen.
Welche Einstellungen müssen gemacht werden, damit die Kommunikation
funktioniert?
Gruß Florian
Fuer die Kommunikation im Beispiel ist
Baud 9600
Bits 8
StopBit 1
voreingestellt.
Hast Du schon mal eine ganz normale serielle Komunikation ueber RS485
zwischen Deinem PC und dem uC probiert, ohne Modbus.
Ich weiss nicht wie Dein USB/RS485 Adapter funktioniert.
Vielleicht versuchst Du es einfach mal mit dem FT232 vom SEDU Board und
RS232.
Da faellt mir gerade ein, es gab mal das Problem, das einige Modbus Poll
Programme nicht mit virtuellen COM Ports arbeiten.
Ist das Windows mit dem du werkelst?
Ju
Hi,
Naja RS232 bringt mich nicht wirklich weiter, da ich eben über den PC
auch auf mehrere Slaves zugreifen will. Der FT232 ist direkt
onboard.....
Ich werde mal noch versuchen im Auschlussverfahren zu schauen wo es
hängt.... evtl. mal einen anderen USB zu RS485 Converter nehmen....
Da ich oft mit Adaptern zu tun hab ist es mir auch bekannt, dass es da
ab und an zu Problemen kommt.
Ansonsten muss ich halt auch mal noch eine andere Hardware nehmen.....
Gruß Flo
Mit welchem Tool hast du den ganzen Code überprüft?
Vielleicht kannst du mir ja auch mal eine compilierte Version anhängen.
Vielleicht läuft da was schief.... Einzige Änderung im Code wäre ja die
oben angesprochene mit PD6 als RS485_send.
Der Adapter blinkt in jedem Fall und es wird mir auch angezeigt, dass
auf TxD was ausgegeben wird aber eben keine Antwort kommt. Dadurch
entsteht ein Time Out.
Baud 9600
Bists 8
Stop Bit 1
Parität: Ohne ?
Inputs:
Slave ID: 11
Functions Code: 4
Startadress: 0
Quantity: 10
Das sollte doch hinhauen oder....von den Konfigurationen
Gruß Flo
Startadress: 0
und
Functions Code: 4
geht in die Hose,
Wie weiter oben schon geschrieben, Modbus faengt
bei 1 an zu zaehlen
und Funktionscode 4 ist READ_INPUT_REGISTERS
will also Input Register lesen die im Beispiel nicht verwendet werden.
Das Beispiel liest und schreibt Holding Register und Coils.
Im Beispiel sind
actProfileData[IDX_DW_Debug01] = 101;
actProfileData[IDX_DW_Debug02] = 102;
actProfileData[IDX_DW_Debug03] = 103;
Holding Register und haben die angegebenen Werte.
die Positionen im Array sind
IDX_DW_Debug01 35
IDX_DW_Debug02 36
IDX_DW_Debug03 37
Somit musst Du modpoll mit
Slave ID: 11
Functions Code: 3
Startadress: 36
Quantity: 3
aufrufen und solltest 101, 102 und 103 zurueck bekommen.
Ju
Hi,
ja inputs wird nicht unterstützt....
aber ich bekomme auch keinen Holding zum laufen.
Slave ID: 11
Functions Code: 3
Startadress: 36
Quantity: 3
Ergebnis: TimeOut
Baud 9600
Bists 8
Stop Bit 1
Parität: Ohne ?
Ja ich werkle mit Windwos.... aber der RS485 Adpater gibt auch saubere
Signale aus, dass sieht man am TxD Pin... nur kommt nix zurück?!
Die Antwort fehlt.
Komme hier auf keinen wirklich grünen Zweig....
Hast du am verlinkten Schaltplan irgendwas gesehen was es sein könnte,
dass die Funktion nicht gewährleistet ist?
Werde mir wohl doch einen anderen AVR schnappen und einen Bustreiber
dran bauen und dann mit dem USB --> RS485 Adapter nochmal versuchen....
wird aber noch bisschen dauern, da ich da sicher nicht alle teile parat
habe.... :)
Gruß Flo
Hi,
habe beim stöbern im Internet den Beispiel Code gefunden und habe
festgestellt das er recht übersichtlich ist.
Allerdings bekomme ich den Modbus Slave nicht zum laufen, vielleicht
kann mir ja jemand helfen oder hat eine Idee wodran es liegen könnte.
Ich benutze ein Stk500 mit einem Atmega32. Habe einen externen Quarz mit
16Mhz angeschlossen.
Als Bustreiber verwende ich einen Max487. RE/ und DE des Bustreibers
habe ich mit PD5 des Stk500 verbunden. RO und DI sind mit PD0 und PD1
(RX und TX) verbunden.
Nun Versuche ich mit Hilfe von Modbus Poll die Holding Register
auszulesen.
Allerdings bekomme ich immer ein Read Error:Break condition.
Habe in Modbus Poll folgende einstellungen vorgenommen:
Connection: Serial Port
- 9600 Baud
- 8 Data Bits
- None Parity
- 1 Stop Bit
Read Write Definition:
Slave ID 11
Function:03
Address: 36
Quantity:3
Scan Rate 1000 ms
Über eine gute Idee oder anmerkung wo mein fehler liegen könnte würde
ich mich freuen.
Gruß Hauke
Hallo Hauke,
die Einstellungen von Modbus Poll sind OK.
Ich tippe auf ein Hardware Problem.
Kannst Du die Signale zum Max487 verfolgen?
Und die Signal A und B (oder + und -) auf der RS485 Leitung.
Evtl vertauscht.
Gruss
Ju
[edit]
check auch die PD0 und PD1 Verbindung zwischen Atmega und MAX
RO sollte auf RXD und DI auf TXD sein.
[noch ein edit]
und an die RE/ und DE Pins des Max sollte ein PullUP oder PullDown hin
Hallo Juergen,
erstma recht vielen dank fuer deine schnelle Antwort.
Habe die Leitungen zwischen dem Atmega und dem MAX nochmals verfolgt.
Die Verdrahtung ist wie du oben geschrieben hast.
Habe das Signal am RXD des Atmegas mal mit einem Oszi aufgezeichnet und
im Anhnag gepostet.
Zusätzlich habe ich ´RE/ und DE des MAX mit einem 2k2 Widerstand auf GND
gelegt.
Allerdings bekomme ich immer noch keine Antwort vom ATmega bin langsam
ein wenig am verzweifeln.
Muss ich vielleicht irgendwelche Fuse Bits setzten? Nutze seit neustem
AVR Studio 5, habe nur unter Tools --> AVR Pogramming --> Fuses --->
SUT_CKSEL die Fuses auf EXTHIFXTALRES_1KCK_64MS gesetzt. Vielleicht habe
ich da auch einen Fehler gemacht?
Über weitere Anregungen, wo ich noch nach fehlern suche könnte würde ich
mich freuen. Wie gesagt bin bei diesem Projekt ein wenig am verzweifeln.
Danke schon mal im vorraus
Gruß Hauke
Fuses, uff, lass mich mal nachdenken.
Ich meine wenn die Clock fuses richtig gesetzt sind, also so das Dein
Atmega mit dem 16MHz Cristal auch mit 16MHz rennt sollte es gut sein.
Allerdings hat der AtMega32 auch noch die JTag Fuse, diese betreffen
aber nur den PortC.
AVR Studio hab ich keine Ahnung, bei mir stehen die
lfuse auf 0xFF
hfuse auf 0xD9
Ju
Moin,
ok die waren richtig gesetzt.
Am code habe ich nichts geändert.
Vielleicht sonst noch eine Idee, wo sich ein Fehler eingeschlichen haben
könnte?
Gruß Hauke
Wenn das soweit alles OK ist, hilft nur zu untersuchen an welchem Punkt
die Komunikation aussteigt.
Ich gehe davon aus dass Du den Code
http://www.mikrocontroller.net/attachment/113765/BSP_Modbus.zip
verwendest.
Als erstes:
rufe den modpoll mal so auf das nur nach einem Word gefragt wird
Also
Connection: Serial Port
- 9600 Baud
- 8 Data Bits
- None Parity
- 1 Stop Bit
Read Write Definition:
Slave ID 11
Function:03
Address: 36
Quantity:1
und im main()
wackelst Du an den entsprechenden Stellen mal an einem Pin des uC's um
zu sehen welcher Teil der Bedingungen erfuellt ist.
Wenn Du siehst das
modbus_processSlaveFrame()
aufgerufen wird dann etwas tiefer gehen und im File JG_Modbus.c
am Ende des Files in der Funktion modbus_processSlaveFrame()
sehen ob der CRC Check durchgeht. Ich hatte schon modpoll Programme die
einen eigenen CRC Key verwendeten.
Wenn der CRC check durchgeht, im selben File in der Funktion
modbus_slave_manage()
1
switch(sft.function){
2
...
3
}
sehen mit welcher Funktionsnummer der modpoll tatsaechlich daherkommt.
Ich hatte es schon, das der Master eigentlich mit
03 READ_HOLDING_REGISTERS
daherkommen sollte
tatsaechlich kam er aber mit
04 READ_INPUT_REGISTERS
daher.
und da in meinem Code nicht alle Funktions Nummern komplett
implementiert sind kann es da unter Umstaenden zu Missverstaendnissen
kommen.
Gruss Ju
Moin,
schön dank für die Anregungen.
Habe den Fehler nun auf dem STK500 festgestellt. Den Grund dafür kenne
ich aber nicht. Habe die Schaltung nochmal auf einem extra Board
aufgebaut und nun funktioniert es.
Allerdings bekomme ich immer nur ein bis 2 Antworten. Den Fehler hierfür
habe ich in der Sende Empfangs Umschaltung festgestellt. Also PD5 bleibt
auf hight Pegel stehen und Schaltet nicht wieder zurück aul low.
Wenn du hier zu noch eine idee hättest wäre ich dir echt dankbar.
Gruß Hauke
Freut mich das es jetzt klappt.
Ich kann mir nicht vorstellen warum das mit dem zurueckschalten in den
Empfangsmodus nicht tut.
Wenn es einmal tut sollte es immer tun, da es per ISR gemacht wird. Wenn
kein bit mehr zu senden ist geht der Pin Low.
im File Modbus_uart.c
Moin,
der Code scheint so weit zu passen.
Habe das Signal an RE/ und DE mal mit dem osi aufgezeichnet.
Nehme ich die Betriebsspannung vom Atmega weg und steck sie wieder ran
bekomme ich immer einmal eine Antwort und wie vermutet schaltet der PD5
nach dem senden nicht wieder ab.
Gruß Hauke
Wenn der Compiler auf speed otimiert ist habe ich keinen Fehler beim
Compilieren, auch wenn ich vorher auf Build --> clean gehe.
Stelle ich allerding ein das nicht optimiert werden soll bekomme ich den
Fehler undefined reference to 'setBitValForSysBit' nehme ich allerdings
das inline vor der funktion weg, so ist auch der Fehler verschwunden.
Das ist soweit OK, lass das inline besser dort und compilire das es
damit lauft.
Ich wollte wissen ob der Compiler evtl. irgendwelche Definitionen
vermisst und folglich nicht die TX ISR anspringt.
Da das aber in Ordnung ist, ist irgendwo anders der Wurm drin.
Es sieht so aus als ob der Atmega noch etwas zum senden hat, dein Master
es aber nicht haben will.
Gruss
Ju
Habe den Signal verlauf von PD5 nochmal aufgezeichnet, so wie es
aussieht scheint der Atmega nach dem ersten senden den PD5 PIN wieder
auf low zu setzen.
Allerdings wird der PIN dann nach dem zweiten senden nicht wieder
zurückgesetzt.
Vielleicht eine Idee woran es liegen könnte?
Der einzigste Punkt im ganzen Code wo der RS485_Send (PD5) auf high
gesetzt wird ist im File
Modbus_uart.c
in der Function
uart_putc()
bevor gesendet wird, und wenn alle Bytes raus sind wird er in der ISR
USART0_TX_vect wieder auf Low gesetzt.
Wenn Du keinerlei Aenderungen am code gemacht hast sollte dieser Pin nur
zum senden High sein.
Durchsuch mal Deinen Code ob Du diesen Pin nicht zufaellig als irgend
einen indikator fuer debug zwecke nimmst oder Du uart_putc() irgendwo
anders noch aufrufst.
Dein Oszi zeigt das der Pin etwa 800ms nach dem er nach dem senden
abgeschaltet wurde wieder auf High geht. Das ist zu lange um eine
Antwort auf ein Packet zu sein.
Das erste High kommt gleich nach der Anfrage, das zweite High kommt 1s
nach der ersten Anfrage.
Das sieht aus als ob Dein modpoll 2 mal fragt, die zweite Antwort jedoch
ignoriert.
Rufe mal Dein modpoll so auf, dass er nur einen Versuch macht.
Ju
Moin,
habe es seit langem mal wieder geschafft am Projekt zu basteln. Habe
einen zweiten Slave aufgebaut und einen Wago Buskoppler als MAster
eingesetzt.
Nach der Pause habe ich den auch den kleinen Fehler im Code gefunden und
zwar in der Modbus_uart.h im Teil wo der Atmega32 definiert wird steht:
#define TXCIE TXEN das TXEN habe ich nun durch TXCIE ersetzt und es
funktioniert.
Nochmals vielen dank für deine Hilfe.
Gruß Hauke
Danke für den Kode!
Ich habe ein Testprogramm auf einem ATmega48 fast ohne Probleme zum
Laufen bekommen.
Ein erster Vorschlag zur Verbesserung besteht darin, die Interruptrate
aus der gesetzten Baudrate abzuleiten:
1
voidinitInterrupts(void)
2
{
3
// TC2 used for Modbus Timeout
4
// CTC mode 32 prescaler
5
// one interrupt per bit length
6
// baudrate is F_CPU / (16*UBRRx)
7
// UBRRx < 500 for common baudrates and F_CPU=8MHz
..bei der Abfrage der Coils bzw. Inputs wird eine falsche Anzahl von
Daten Bytes zurück gesendet.
Der Fehler scheint durch die Aufsummierung der Datenmenge + Startadresse
in der for{} Schleife zu entstehen, da die Variable ic mit 0 und nicht
mit der Startadresse beginnt, sollte hier die Datenmenge reichen!
geänderter Code:
static int response_io_status(sft_t *sft, uint8_t *stab,
uint8_t *response, int offset) {
uint8_t sBit = sft->address;
uint8_t startByte = (uint8_t)(sBit / 8);
uint8_t nOBytes = (uint8_t)(sft->nb / 8) + ((sft->nb % 8)?1:0);
//uint8_t bytesToThread = nOBytes + ((sft->address % 8)?1:0);
uint8_t ic;
uint8_t destByte = 0, intermByte = 0;
for (ic = 0; ic < nOBytes; ic++){
// getting the first byte where we have to get Bit's from
..
Hallo zusammen,
ich habe das ganze mal probiert und komme leider nicht weiter.
Ich bin so vorgegangen:
-Neues Projekt im AVR Studio erstellt (Atmega16)
-Dateien dem Projekt hinzugefügt.
-F_CPU auf 10MHZ gesetzt in den Projekteigenschaften.
-Timereinstellungen angepasst:
void initInterrupts(void) {
// TC2 used for Modbus Timeout and sysSec Counter
TCCR2 |= ((1<<CS21)); //Prescaler auf 8 setzten
OCR2 = 116; //Wert vorladen
TIMSK |= (1<<OCIE2);
}
In der Beispieldatei auf RS232 umgestellt.
rs323_init(UART_BAUD_SELECT(9600,F_CPU));
statt
rs485_init(UART_BAUD_SELECT(9600,F_CPU));
Compilieren erzeugt keine Fehler und läuft sauber durch. Die Fuses sind
auf externen Quarz gesetzt. JTAG ist aus.
Das Board hängt direkt über RS232 am Rechner dran (MAX232 dazwischen)
Hardwareaufbau funktioniert mit einfachem Sende/Antworte Programm. Denke
daran liegt es nicht.
Hab ich irgendwas noch übersehen?
Besten Dank
F.
Juergen G. schrieb:> Modbus RTU ist zeitabhaengig, die Pausen muessen exakt eingehalten> werden, sonst wird das Paket nicht als gueltig erkannt.>> Die Timer in meinem Beispiel sind glaube ich fuer einen 16MHz Crystal> konfiguriert. Da also mal als erstes ansetzen und deinen Timer so> konfigurieren das er der Zeit von einem Bit Deiner serial Uebertragung> entspricht.
Hallo,
ich weiß, dass es ein alter Thread ist, aber ich habe eine Frage zur
Bitzeit.
Wie ist die zu verstehen? Nach meiner Rechnung passen die Einstellungen
nicht zusammen, es funktioniert aber...
Ich habe das Beispiel erfolgreich auf einem AT90CAN als RS232 Variante
mit 9600 Baud und externen 16 MHz laufen. Vielen Dank für das kurze
Beispiel!
Timer zwei ist auf Prescaler 8 gestellt und wird mit 32 vorgeladen.
Macht nach meiner Rechnung 16MHz/8=2MHz.
(1/2MHz)*32=16us
9600 Baud (in bits per second laut Datenblatt) ist 1/9600=104us.
Irgendwo muss ich da etwas falsch verstehen.
In der Datei JG_Modbus.c muss in der Funktion modbus_slave_manage() ein
"break" entfernt werden. Es verhindert den Zusammenbau einer gültigen
Nachricht und liefert nur FF FF als Antwort. Das richtige break ist
schon da.
Hallo,
hätte hier noch etwas: https://github.com/mbs38/yaMBSiavr
Eine Implementierung, die auch die Function Codes "Force Multiple Coils"
und "Force Multiple Registers" unterstützt. Allerdings muss man ihr die
verschiedenen Baudraten noch per Hand beibringen, da sie nicht auf
Fleurys Library basiert. Dafür ist die Lib recht gut kommentiert.
Dabei ist auch ein Beispiel für den Atmega88, das bei mir zu 2,9k
kompilliert.
Vielleicht nützt das ja jemandem :)
CB
Hallo Christian,
die genannte Implementierung stammt von mir. Danke für die Blumen. Ich
hab die Verlinkung hier mal als Anlass genommen mal ein Release zu
machen und ein paar Sachen sauberer reinzuschreiben und so. Falls es
jemand brauchen kann:
https://sourceforge.net/projects/yambsiavr/
Hallo,
ich versuche seit einigen Tagen meinen ATMega644PA den Modbus
beizubringen. Ich plane ein derzeit ein Projekt mit dem ATMega und einem
Siemens OP177.. die Kommunikation läuft deshalb zwangsweise über den
Modbus.
Nun versuche ich schon einige Tage mich durch diverse Beispiele zu
wuseln um einen funktionierenden Code hinzubekommen.
Ich arbeite derzeit mit einem Pollin Evaluation Board und einem USB zu
RS485-Adapter. Habe jetzt den ATMega mit TX/RX und Richtungspin an einen
MAX485 angeschlossen und diesen mit dem USB->RS485 Adapter verbunden.
Leider bekomme ich über Modbus Poll nur ein TimeOut.
Angehängt habe ich meine beiden Projekte mit den jeweiligen Beispielen.
ModbusPoll:
Baudrate je nach Bsp. 9600 oder 38400
8 Bit Daten
1 Stoppbit
keine Parity
keine Flowcontrol
Hallo Gorden,
ich hab mir MB-yaMBSiavr.zip angeschaut.
Dein Timer sorgt bei 16Mhz, und Prescaler 8 für einen Interrupt alle
128µs, anstatt alle 102,5µs wie im Beispiel. Daher solltest du in der
yaMBSiavr.h in den Zeilen
#define modbusInterFrameDelayReceiveStart 16
#define modbusInterFrameDelayReceiveEnd 18
#define modbusInterCharTimeout 7
die Werte auf 14, 15 und 6 ändern, damit das wieder passt.
Das sollte allerdings nicht der Grund gewesen sein, warum es nicht
funktioniert :P
Aber:
Du weißt, dass der UART1 verwendet wird und nicht UART0?
In Modbuspoll hast du auch die richtige Device Adresse (hier 1) und auch
nur die im Beispiel vorhandenen Register/Coils oder was auch immer
ausgewählt?
Viele Grüße, M
Hi Max,
danke für die schneller Antwort, und sorry dass ich dich zuerst per PN
angeschrieben hab und dann das hier noch gepostet habe.
Zur Sache: Erstmal danke für die Einstellungsanpassung.
Auf die Gefahr hin, dass ich jetzt (jetzt erst recht wo ich es noch
schreibe) gesteinigt werde: der ATMega644PA hat ja zwei USARTS und kein
UART.. nun ist in der Library doch alles so angepasst, dass die
Adressierung hinhaut, gibt ja auch beim Kompilieren keine Fehler..
soweit so gut.
Da du sagst dass USART1 benutzt wird:
USART1 geht laut Datenblatt auf den PORTD dementsprechend habe ich die
PORTD PIN 0 und 1 (RX/TX) auch an den MAX485 angeschlossen.. sollte also
auch passen.
Das Modbus Poll-Programm hab ich entsprechend der in deinem Beispiel
definierten Holdingregister eingestellt.. also 0..3, sollte diese
Adresse falsch sein, sollte ich doch aber Read/Write-Error bekommen und
kein plumpes Timeout oder?
Slaveadresse 1 ist soweit auch definiert und die entsprechenden
Einstellungen wie oben zusätzlich habe ich den USB-COM-Anschluss genauso
eingestellt, falls das nicht eh schon das modbus poll programm macht.
Ich werde gegen 18 Uhr nochmal die angepassten Daten einspeisen und dann
testen. Es könnte auch sein, dass mein MAX485 irgendeinen defekt. Die
Pegel an A/B ändern sich jedenfalls nicht wenn ich Polle, aber ich messe
auch nur mit einem Multimeter, habe leider kein Oszi zuhause, wenn ich
das mal im Labor teste, was müsste ich da "ungefähr" auf dem Oszi sehen?
Gorden H. schrieb:> PORTD PIN 0 und 1 (RX/TX)
Nope. PD2 und PD3 sind es.
Und das wird dann noch mit dem TXenable/RXdisable kollidieren. In der
yaMBSiavr.h steht:
/*
* Definitions for transceiver enable pin.
*/
#define TRANSCEIVER_ENABLE_PORT PORTD
#define TRANSCEIVER_ENABLE_PIN 2
#define TRANSCEIVER_ENABLE_PORT_DDR DDRD
Das musst du dann noch auf irgendeinen anderen freien Pin deiner Wahl
umkonfigurieren und die beiden RXdisable/TXenable pins des MAX485 da
dran anschließen.
M
So ich bin jetzt wieder am Objekt. Danke für die Lesenachhilfe, manchmal
ist man einfach zu blind :(.
Habe jetzt folgenden Aufbau:
1
ATMega MAX485 USB->RS485-Adapter
2
________
3
PD2 1|RO Vcc|8 5V
4
| |
5
PD7 2|/RE B|7 485-
6
| |
7
PD7 3|DE A|6 485+
8
| |
9
PD3 4|DI GND|5 GND verbunden mit Board GND
10
|________|
11
12
Vcc und GND hängt noch ein 10µF
13
Muss an PD7 ein Pullup/down Widerstand sein?
Habe den Pin in der yaMBSiavr.h entsprechend angepasst auf 7.
Somit sollte der HW-Aufbau jetzt ja endlich auch richtig sein
(hoffentlich)
Leider bekomme ich über den Modbuspoll weiterhin nur ein Timeout.
Im Ruhezustand (USB-RS485 angeschlossen und verbunden mit dem Board,
aber kein Modbuspoll) messe ich jeweils gegen GND:
RO: 4,90 V
DI: 4,90 V
A: 0,30 V
B: 0,17 V
Wenn ich mit Modbuspoll polle genauso. Sind die Pegel an A/B nicht
irgendwie zu niedrig, bzw. die an PD 2/3 zu hoch? Irgendwie hab ich das
Gefühl, dass sich alle beteiligten Geräte gegen mich verschworen haben.
Gorden H. schrieb:> Muss an PD7 ein Pullup/down Widerstand sein?
nein.
Gorden H. schrieb:> RO: 4,90 V> DI: 4,90 V> A: 0,30 V> B: 0,17 V
Ist völlig normal. Hast halt keine Bias-Schaltung gebaut für den Bus..
http://www.ni.com/images/support/us/bias.gif
Dürfte aber trotzdem funktionieren.
Der Adapter müsste theoretisch gehen.
Nimm mal HTERM oder so und schick mal ne Modbus Message per Hand. Vorher
in yambsiavr.c einfach die crc funktion so verändern, dass sie immer
0x1234 liefert. Dafür musst du nur out=0x1234 einkommentieren.
Dann einfach mal 01 03 00 00 00 03 34 12 hinschicken. Dann müsste was
zurückkommen...
Ansonsten mal mit nem zweiten seriell->usb wandler mal direkt auf den RO
vom MAX485 hängen und gucken was da so kommt. Da sollte das gleiche
ankommen. Wenn das schon nicht der Fall ist naja ne... ^^
Huhu,
zum HTerm:
Port COM4 (ist der USB 485 adapter) Data 8 Stop 1 Parity None und kein
CTS Flow Control...
Wenn ich Connecte sehe ich keine CTS DSR RI DCD LED - aber sind
vielleicht eh nie an bei der Hardware..
Wenn ich jetzt "01 03 00 00 00 03 34 12" sende über "HEX" und dann Enter
so dass es im Transmitted steht... habe also das Gesendet(?), erhalte
aber nichts zurück.. :(
Einen zweiten USB/Serielladapter habe ich leider nicht rumfliegen, der
hier war schon eine Neuanschaffung für den Zweck.
Gorden H. schrieb:> Wenn ich jetzt "01 03 00 00 00 03 34 12" sende über "HEX" und dann Enter> so dass es im Transmitted steht... habe also das Gesendet(?), erhalte> aber nichts zurück.. :(
Dann hast du es gesendet.
Ansonsten halt (Speicher-)Oszi an RO halten. So richtig gut sehen ob was
Vernünftiges kommt kann man dann mit Hirnschmalz+Speicheroszi oder nem
weiteren FTDI Ding:
> Einen zweiten USB/Serielladapter habe ich leider nicht rumfliegen, der> hier war schon eine Neuanschaffung für den Zweck.
Dann würde ich noch einen zweiten anschaffen. Kann man immer brauchen.
Kostenpunkt unter 5 Euro bei ebay.
m.n. schrieb:> Max B. schrieb:>> Dann würde ich noch einen zweiten anschaffen.>> Aus Erfahrung: um Digitus mache ich schon seit vielen Jahren einen> weiten Bogen.
Ich meinte irgendein Board mit einem TF232 drauf...
Also um das ganze nochmal abzurunden.. am Code liegt es dann wohl nicht
mehr? und Hardware ist soweit auch ok. dann besorg ich mir wohl nochmal
nen neuen Max485 da bei dem den ich hab nicht zu 100% klar ist, ob er
überhaupt noch ganz funktionabel ist.
Das Problem ist, dass ich mit der ganzen Sache schon unter Zeitdruck
stehe, da tut quasi jeder Tag an dem ich nicht weiter komme weh :/
Aber trotzdem nochmal ein großes Danke an euch, dass ihr mich soweit an
die Hand genommen habt. Bin nur leider grad an dem Punkt wo ich
überlegen muss, ob ich die RS485/Modbus-Sache begrabe und stattdessen
auf ein herkömmliches 0815-LCD+Matrixtastertur downgrade, was ich direkt
oder über einen zweiten µC anspreche.
Hi, vielleicht würde das als Ersatz für meinen blanken max485 dienen um
den fehlerhaften maxi auszuschließen. hoffe der Link geht so.
http://www.segor.de/#Q=RS485-TTLModul&M=1
Der Direktlink geht leider nicht. Einmal auf neuer Katalog gehen dann
nach RS485 suchen, dann taucht das RS485 TTl Modul auf.
Und zusätzlich nochmal einen anderen USB RS485 Wandler meinst du?
Einfach mal so gefragt.
Wie lang sind denn Deine Leitungen zwischen den beiden "Endgeraeten"
sind die Leitungen verdrillt
hast Du Abschluss R's dran?
wenn die Leitungen kurz sind und keine R's dran sind, haben es die Max
schwer da was richtiges zu erkennen.
Das Du einen Max gegrillt hast sehe ich eher als unwarscheinlich an. Die
MAX sind meiner Erfahrung nach sehr robust, da brauchs schon einiges um
die kaputt zu machen.
Ju
In der Tat sind die Leitungen zur Zeit zum Testen sehr kurz, nur ein
paar Zentimeter um genau zu sein. Hätte nicht gedacht, dass sich das
noch negativ auswirken könnte.
Was für einen Abschlusswiderstand würdest du empfehlen, habe jetzt
zwischen 100 und 120 Ohm gelesen die ich dann am Max an den A/B
anschließe, und das gleiche am USB-RS485 Wandler?
Zum Grillen: Das Siemens OP177 hat eine serielle Schnittstelle mit SubD9
Buchse. Das Pinout ist abweichend zum RS422/485 und zusätzlich ist auf
einem Pin auch noch +24V. Das hab ich aber quasi in meiner Naivität in
den Anfängen nicht überprüft, deshalb kann es sein, dass der MAX
(welcher ineinem rs458/rs232 Adapter verbaut war gegrillt wurde, habe
den jetzt quasi ausgebaut um mir die RS232/485 Wandlung als Fehlerquelle
zu sparen, und den ATMega direkt über RS485 kommunizieren zu lassen.
Gorden H. schrieb:> Bin nur leider grad an dem Punkt wo ich> überlegen muss, ob ich die RS485/Modbus-Sache begrabe und stattdessen> auf ein herkömmliches 0815-LCD+Matrixtastertur downgrade, was ich direkt> oder über einen zweiten µC anspreche.
Ich kenn weder das Siemens Gerät noch Deine Anforderungen. Aber wenn
eine Lösung mit eigenem µC reichen würde, würde ich das vermutlich so
machen. Falls es noch woanders klemmt, hast Du größeren Spielraum bei
der Anpassung.
m.n. schrieb:> Max B. schrieb:>> TF232>> ?
Offensichlich ein unkorrigierter Tippfehler, aber auch zu TF232 gibt es
etliche Treffer bei der Suche.
Aktuell möchte hier jemand ein 433mhz-Signal über Antenne empfangen.
Wieviel km Länge muß diese wohl haben?
Die Angabe des Siemens-Gerätes war auch nur der Vollständigkeit halber.
Sinn ist halt eine Visualisierung von Füllständen von eingegrabenen
Tanks. Diese sollen dann befüllt/entleert werden können bzw. der
Gartenteich und die Rasensprenger durch diese Tanks gespeist werden. Mit
einem kleinen 2 Zeilen Display wäre das mühselig. Die HMI würde da schon
viel Comfort bieten zur Auswahl von Ventilen etc., und ich kann diese
halt gut selbst Projektieren aus Erfahrung.
m.n. schrieb:> Max B. schrieb:>> TF232>> ?> Ich lerne gerne dazu.
Sorry. FTDI FT232 war gemeint.
Gorden H. schrieb:> Hi, vielleicht würde das als Ersatz für meinen blanken max485 dienen um> den fehlerhaften maxi auszuschließen. hoffe der Link geht so.> http://www.segor.de/#Q=RS485-TTLModul&M=1> Der Direktlink geht leider nicht. Einmal auf neuer Katalog gehen dann> nach RS485 suchen, dann taucht das RS485 TTl Modul auf.>
Kannste machen. Kannst auch wenns drängt einfach zu Conrad oder so gehen
und einen max481, max485 oder ähnlich verlangen.
> Und zusätzlich nochmal einen anderen USB RS485 Wandler meinst du?
USB zu UART auf TTL Pegel. Sowas kann man zum Testen eben auch ohne
MAXxxx an den Atmel hängen.
jup schrieb:> Wie lang sind denn Deine Leitungen zwischen den beiden "Endgeraeten"> sind die Leitungen verdrillt> hast Du Abschluss R's dran?
Bei der Baudrate und Kabellänge ist Verdrilltheit und
Reflektionsfreiheit durch Abschluss mit Sicherheit egal. Das Biasing
mitsamt einem Widerstand in der Mitte des Teilers sollte man aber auf
jeden Fall machen, da es ja vorkommen kann, dass keiner der auf dem Bus
vorhandenen Treiber gerade nicht hochohmig ist.
jup schrieb:> Das Du einen Max gegrillt hast sehe ich eher als unwarscheinlich an. Die> MAX sind meiner Erfahrung nach sehr robust, da brauchs schon einiges um> die kaputt zu machen.
Kommt drauf an. RS485 Spec verlangt, dass man +-12V anlegen kann, ohne,
dass etwas kaputtgeht. Nicht jedoch 24V. Damit habe ich auch schon mal
um die 20 MAX481 auf einem Bus auf einen Schlag gegrillt...
Es gibt natürlich zB. sowas wie MAX13442E, an den man sogar +-80V
anlegen kann :P
Gorden H. schrieb:> Zum Grillen: Das Siemens OP177 hat eine serielle Schnittstelle mit SubD9> Buchse. Das Pinout ist abweichend zum RS422/485 und zusätzlich ist auf> einem Pin auch noch +24V.
Sicher, dass das Ding Modbus spricht? Hab das nicht direkt finden
können.
Max B. schrieb:> Gorden H. schrieb:>> Zum Grillen: Das Siemens OP177 hat eine serielle Schnittstelle mit SubD9>> Buchse. Das Pinout ist abweichend zum RS422/485 und zusätzlich ist auf>> einem Pin auch noch +24V.>> Sicher, dass das Ding Modbus spricht? Hab das nicht direkt finden> können.https://cache.industry.siemens.com/dl/files/461/21084461/att_82592/v1/hmi_tp177a_tp177b_op177b_operating_instructions_en_US_en-US.pdf
Lt. Datenblatt Seite 29 Spricht es Modbus unter Verwendung eines
Siemens-Adapters. Meine Herangehensweise war bisher die: solange die
Kommunikation zwischen PC und ATMega über RS485 und Modbus nicht
funktioniert, will ich es erst gar nicht am Panel versuchen, weil das
Gerät auch keinerlei Debug-Funktionen bietet um zu überprüfen ob
überhaupt was passiert. Das einzige was man sich anzeigen lassen kann,
ist ob der anzuzeigende Wert gelesen wird (wenn keine Kommunikation
werden nur "####" angezeigt sonst halt der Wert).
Aber vermutlich werde ich mal parallel als Notlösung mit einem kleinen
Display anfangen, vielleicht mit einem zweiten µController für
Matrixtastatur und LCD. eine Kommunikation ATTiny<->Atmega oder
Atmega<->Atmega sollte ja hoffentlich für mich hinzubekommen sein.
Gorden H. schrieb:>> Sicher, dass das Ding Modbus spricht? Hab das nicht direkt finden>> können.>> https://cache.industry.siemens.com/dl/files/461/21084461/att_82592/v1/hmi_tp177a_tp177b_op177b_operating_instructions_en_US_en-US.pdf>> Lt. Datenblatt Seite 29 Spricht es Modbus unter Verwendung eines> Siemens-Adapters.
Aha. Da steht nicht, dass das Ding Modbus-Master sein kann. Hört sich
für mich mehr so an, als würde es ein Client für eine der in dem
Tabellenfeld daneben aufgeführten SPSen sein.
Gorden H. schrieb:> Aber vermutlich werde ich mal parallel als Notlösung mit einem kleinen> Display anfangen, vielleicht mit einem zweiten µController für> Matrixtastatur und LCD. eine Kommunikation ATTiny<->Atmega oder> Atmega<->Atmega sollte ja hoffentlich für mich hinzubekommen sein.
Kannste prinzipiell auch Modbus für nehmen.
Also ich nutze für Modbus am AVR (ATmega88 und ATtiny841) immer
freemodbus.
Sowohl für RTU als auch TCP. Funktioniert super.
http://www.freemodbus.org/
Für den PC nimm ich libmodbus. Z.B. für ein Modbus-Tester, der mir dann
verschiedene Funktionen nacheinander durchtestet. Also
selbstgeschriebene Programme.
http://libmodbus.org/
Und als USB/RS485-Adapter den
http://www.amazon.de/In-Circuit-901-274-USB-RS485-Adapter-Blau-Gr%C3%BCn/dp/B00I9H0998
Der hat die Bias-Schaltung auch schon mitdrin.
Wenn das Panel ein Client/Master ist. Dann muss der AVR natürlich
Server/Slave sein.
Mathias O. schrieb:> Also ich nutze für Modbus am AVR (ATmega88 und ATtiny841) immer> freemodbus.> Sowohl für RTU als auch TCP. Funktioniert super.> http://www.freemodbus.org/
Hab ich auch schon benutzt. Habe dann dennoch eine eigene
Implementierung speziell für den AVR geschrieben, weil ich es auch auf
einem in einem Gerät bereits vorhandenen attiny2313 laufen lassen wollte
- dafür ist freemodbus aber zu bloated.
Mathias O. schrieb:> Für den PC nimm ich libmodbus. Z.B. für ein Modbus-Tester, der mir dann> verschiedene Funktionen nacheinander durchtestet. Also> selbstgeschriebene Programme.> http://libmodbus.org/
Schöner ist natürlich eine fertige GUI zum Testen. Ich hab auch immer
einfach python script geschrieben mit pymodbus dahinter.
Hast du eine nette GUI zur Hand?
Ja tut es. Stürzt allerdings sehr gerne ab und verliert manchmal Pakete
wenn man es benutzt um mal richtig Durchsatz zu machen. Habe mir dann
doch ein paar kleine Skripte mit modpoll geschrieben - zumindest für die
Performance-Tests :)
Hans Günther schrieb:> kann mir jemand sagen wie man diese Library> https://github.com/mbs38/yaMBSiavr) bei Atmel Studio 7 einbindet?
Einfach die .h und die .c Datei per Drag&Drop rechts in den
Library-Explorer von AS7 ziehen. Im code dann #include "yambsiavr.h" e
voila :)