Aktyw Forum

Zarejestruj się na forum.ep.com.pl i zgłoś swój akces do Aktywu Forum. Jeśli jesteś już zarejestrowany wystarczy, że się zalogujesz.

Sprawdź punkty Zarejestruj się

Delphi->RS 232->Modbus->RTU

tobiasz0606
-
-
Posty:3
Rejestracja:25 kwie 2007, o 13:01
Lokalizacja:Grudziądz
Delphi->RS 232->Modbus->RTU

Postautor: tobiasz0606 » 25 kwie 2007, o 13:16

Witam wszystkich.

Mój problem jest następujący. Próbuję się skomunikować ze sterownikiem po RS po protokole MODBUS.

Mój sterownik jeden znak zapisuje na 4 bitach. Interesują mnie adresy pamięci powyżej adresu 3000h. Handshaking chyba mam sprzętowy. To co wymyśłiłem do tej pory zapisałem poniżej. $ ostanie znaki to obliczenia z CRC16. Na razie liczyłem je osobno w innym programie. Chociaz procedure też mam rozpisaną.

Kod: Zaznacz cały

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) OpenComm: TButton; Send: TButton; Receive: TButton; CloseComm: TButton; RichEdit1: TRichEdit; procedure CloseCommClick(Sender: TObject); procedure OpenCommClick(Sender: TObject); procedure SendClick(Sender: TObject); procedure ReceiveClick(Sender: TObject); private { Private declarations } procedure Write_Comm(hCommDev: THANDLE; nNumberOfBytesToWrite: DWORD); procedure Read_Comm(hCommDev: THANDLE; Buf_Size: DWORD); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} const // -- wartości znaczników sterujących portu szeregowego -- dcb_fBinary = True; dcb_fParity = False; DTR_CONTROL_HANDSHAKE = False; RTS_CONTROL_HANDSHAKE = True; dcb_fOutXCts = True; dcb_fInX = False; dcb_fErrorChar = False; dcb_fNull = False; dcb_AbortOnError = False; cbInQueue = 15; cbOutQueue = 15; var query : PChar = '0503300000018A8E'; // przykładowe zapytanie Buffer_O : ARRAY[0..cbOutQueue] of Char; // bufor wyjściowy Buffer_I : ARRAY[0..cbInQueue] of Char; // bufor wejściowy Number_Bytes_Read : DWORD; hCommDev : THANDLE; lpFileName : PChar; fdwEvtMask : DWORD; Stat : TCOMSTAT; Errors : DWORD; dcb : TDCB; procedure TForm1.Write_Comm(hCommDev: THANDLE; nNumberOfBytesToWrite: DWORD); var NumberOfBytesWritten : DWORD; begin WriteFile(hCommDev, Buffer_O, nNumberOfBytesToWrite, NumberOfBytesWritten, NIL); WaitCommEvent(hCommDev, fdwEvtMask, NIL); end; //-------------------------------------------------------------------- procedure TForm1.Read_Comm(hCommDev: THANDLE; Buf_Size: DWORD); var nNumberOfBytesToRead: DWORD; begin ClearCommError(hCommDev, Errors, @Stat); if (Stat.cbInQue > 0) then begin if (Stat.cbInQue > Buf_Size) then nNumberOfBytesToRead := Buf_Size else nNumberOfBytesToRead := Stat.cbInQue; ReadFile(hCommDev, Buffer_I, nNumberOfBytesToRead, Number_Bytes_Read, NIL); end else Number_Bytes_Read := 0; end; //-------------------------------------------------------------------- procedure TForm1.CloseCommClick(Sender: TObject); begin CloseHandle(hCommDev); Application.Terminate(); end; procedure TForm1.OpenCommClick(Sender: TObject); begin lpFileName := 'COM1'; hCommDev := CreateFile(lpFileName, GENERIC_READ or GENERIC_WRITE,0, NIL, OPEN_EXISTING, 0, 0); if (hCommDev <> INVALID_HANDLE_VALUE) then begin SetupComm(hCommDev, cbInQueue, cbOutQueue); // dcb.DCBlength := sizeof(dcb); GetCommState(hCommDev, dcb); dcb.BaudRate := CBR_9600; //-- przykładowe ustawienia znaczników sterujących DCB -- // dcb.Flags := RTS_CONTROL_DISABLE or dcb_fParity; dcb.Parity := NOPARITY; dcb.StopBits := ONESTOPBIT; dcb.ByteSize := 4; SetCommState(hCommDev, dcb); GetCommMask(hCommDev, fdwEvtMask); SetCommMask(hCommDev, EV_TXEMPTY); end else case hCommDev of IE_BADID: MessageDlg('Niewłaściwa nazwa portu '+lpFileName+ ' lub jest on aktywny ', mtError, [mbOk], 0); end; end; procedure TForm1.SendClick(Sender: TObject); begin StrCopy(Buffer_O, query); RichEdit1.Text := Buffer_O; Write_Comm(hCommDev, StrLen(Buffer_O)); FlushFileBuffers(hCommDev); end; procedure TForm1.ReceiveClick(Sender: TObject); begin Read_Comm(hCommDev, SizeOf(Buffer_I)); RichEdit1.Text := Buffer_I; end; end.
Co robie nie tak??
Pomocy
Ostatnio zmieniony 25 kwie 2007, o 18:46 przez tobiasz0606, łącznie zmieniany 1 raz.

Jurek Szczesiul
-
-
Posty:175
Rejestracja:10 paź 2003, o 20:44
Lokalizacja:Białystok
Kontaktowanie:

Postautor: Jurek Szczesiul » 30 kwie 2007, o 08:57

Cześć
Tak na pierwszy rzut oka : port otwierasz w trybie synchronicznym
a potem używasz funkcji asynchronicznej Wait.. - tak to nie zadziała.

BTW - sama obsługa portu i tak nie wystarczy - potrzebny protokół
MODBUS. Gotowe komponenty nie leżą na ulicy - ale ktoś w sieci
oferował udostępnienie swoich - nie wiem czy aktualne.

Pozdrowienia Jurek S.

tobiasz0606
-
-
Posty:3
Rejestracja:25 kwie 2007, o 13:01
Lokalizacja:Grudziądz

Postautor: tobiasz0606 » 1 maja 2007, o 17:53

Dlaczego twierdzisz, że potrzebuje protokołu MODBUS? Znam kształt ramki, prędkości i calą resztę parametrów transmisji.

Jeśli chodzi o ta transmisje synchroniczna i asynchroniczną to rzeczywiście masz rację. Myślisz, że powinienem się zająć wyłącznie asynchroniczną i jak wtedy otworzyć port?

Jurek Szczesiul
-
-
Posty:175
Rejestracja:10 paź 2003, o 20:44
Lokalizacja:Białystok
Kontaktowanie:

Postautor: Jurek Szczesiul » 2 maja 2007, o 15:52

Dlaczego twierdzisz, że potrzebuje protokołu MODBUS? Znam kształt ramki, prędkości i calą resztę parametrów transmisji.
Przeważnie do obsługi rs-a bierze sie jakiś gotowy komponent Delphi ( np. ComPort ) albo
biblioteki ( np. bezpłatna obecnie TurboPower ).
Tak jest szybciej - chyba, ze chcesz samemu w szczegółach "rozpracować" WinApi obsługi portu.
Natomiast nawet używając komponentu trzeba samodzielnie rozpisać obsługę protokołu ( poskładanie ramek, kodowanie , CRC itd. ). Ludzie czasem szukają gotowego komponentu MODBUS, który to wszystko ma juz "zaszyte", a takich jakoś nie widać - i stąd moja uwaga
j.w. W tym przypadku uwaga na wyrost i trochę myląca - bo Ty już te szczegóły protokołu
znasz.

Obsługa synchroniczna czy asynchroniczna portu da ( przy niewielkich i powolnych pakietach modbusowych ) taki sam efekt - asynchroniczna jest po prostu bardziej elegancka i oszczędna z punktu widzenia OS - natomiast z pewnością tych technik nie należy mieszać.

Pozdrowienia Jurek S.

tobiasz0606
-
-
Posty:3
Rejestracja:25 kwie 2007, o 13:01
Lokalizacja:Grudziądz

Delphi->RS 232->Modbus->RTU

Postautor: tobiasz0606 » 4 maja 2007, o 09:16

Dzięki bardzo za Twoją aplikację. Mimo, że nie udało mi się z jej pomocą połączyć ze z tego względu, ze znak przesyłany przez Twój program jest zapisywany na innej ilości bitów bardzo mi pomogła.

Walczę dalej... ostatnia wersja mojego programu wygląda następująco:

Kod: Zaznacz cały

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) Button1: TButton; RichEdit1: TRichEdit; Button2: TButton; procedure Button1Click(Sender: TObject); procedure CRC16(len:integer); procedure Write_Comm(hCommDev: THANDLE; nNumberOfBytesToWrite: DWORD); procedure Read_Comm(hCommDev: THANDLE; Buf_Size: DWORD); procedure Button2Click(Sender: TObject); function int2bin(int:int64):string; function int2hex(int:integer):string; function bin2int(binary:string) :int64; function poteN(liczba:int64;do_potegi:integer=2):int64; private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} const // -- wartości znaczników sterujących portu szeregowego -- dcb_fBinary = True; dcb_fParity = False; dcb_fOutxCtsFlow = True; dcb_fOutxDsrFlow = True; dcb_fDSRSensitive =False; dcb_fDtrCOntrol =True; dcb_fTXContinueOnXoff = False; // DTR_CONTROL_ENABLE = True; // RTS_CONTROL_ENABLE = True; // DTR_CONTROL_HANDSHAKE = True; // RTS_CONTROL_HANDSHAKE = True; dcb_fOutX = False;//ok dcb_fInX = False;//ok dcb_fErrorChar = False; dcb_fNull = False; dcb_AbortOnError = False; cbInQueue = 16; cbOutQueue = 16; var ramka:String='050330000001'; ramka_ost:String; Buffer_O : array[1..cbOutQueue] of Char; // bufor wyjściowy Buffer_I : ARRAY[1..cbInQueue] of Char; // bufor wejściowy Data: array[1..8] of Integer; hCommDev : THANDLE; lpFileName : PChar; fdwEvtMask : DWORD; Number_Bytes_Read : DWORD; Stat : TCOMSTAT; Errors : DWORD; dcb: TDCB; procedure TForm1.Write_Comm(hCommDev: THANDLE; nNumberOfBytesToWrite: DWORD); var NumberOfBytesWritten : DWORD; begin WriteFile(hCommDev, Buffer_O, nNumberOfBytesToWrite, NumberOfBytesWritten, NIL); WaitCommEvent(hCommDev, fdwEvtMask, NIL); end; procedure TForm1.Read_Comm(hCommDev: THANDLE; Buf_Size: DWORD); var nNumberOfBytesToRead: DWORD; begin ClearCommError(hCommDev, Errors, @Stat); if (Stat.cbInQue > 0) then begin if (Stat.cbInQue > Buf_Size) then nNumberOfBytesToRead := Buf_Size else nNumberOfBytesToRead := Stat.cbInQue; ReadFile(hCommDev, Buffer_I, nNumberOfBytesToRead, Number_Bytes_Read, NIL); end else Number_Bytes_Read := 0; end; procedure TForm1.Button1Click(Sender: TObject); var i:Integer; s:String; begin Data[1]:=5; Data[2]:=3; Data[3]:=48; Data[4]:=0; Data[5]:=0; Data[6]:=32; CRC16(6); ramka:=''; ramka_ost:=''; // wypelnienie ramki for i:=1 to 8 do begin // wypelnienie ramki po osiem bitów 2 znakami 05 03 30 00 00 20 (4A 96)<-CRC16 ramka:=ramka+int2bin(DATA[i]); end; //zamiana na znaki 16 znaków szestastkowych po jednym na 4 bity for i:=1 to 16 do begin s:=Copy(ramka, (8*i-7), 8 ); ramka_ost:=ramka_ost+int2hex(bin2int(s)); end; // konwersja string na char for i:=1 to length(ramka_ost) do Buffer_O[i]:= ramka_ost[i]; lpFileName:='Com1'; hCommDev:=CreateFile(lpFileName, GENERIC_READ or GENERIC_WRITE,0,NIL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0); if (hCommDev=Invalid_Handle_Value) then begin MessageDlg('Niewłaściwa nazwa portu '+lpFileName+ ' lub jest on aktywny' , mtInformation, [mbOk], 0); end; GetCommState(hCommDev,dcb); // parametry transmisji dcb.BaudRate:= CBR_9600; dcb.Parity:= NOPARITY; dcb.StopBits:=1; ///ONESTOPBIT; dcb.ByteSize:=8; SetCommState(hCommDev,dcb); RichEdit1.Text := Buffer_O; Write_Comm(hCommDev,16); // szesnascie znaków po 2 na 8 bitach danych.. FlushFileBuffers(hCommDev); end; procedure TForm1.CRC16(len:integer); var crc: word; n,i: integer; b:byte; begin crc := $FFFF; for i:=1 to len do begin b:=Data[i]; crc := crc xor b; for n:=1 to 8 do begin if (crc and 1)<>0 then crc:=(crc shr 1) xor $A001 else crc:=crc shr 1; end; end; Data[len+1]:=crc and $ff; Data [len+2]:=crc shr 8; end; function TForm1.int2bin(int:int64):string; var strtemp:string; s:string; begin while int>0 do begin insert(inttostr(int mod 2),strtemp,1); int:= int div 2; end; s:='0'; if Length(strtemp)<8 then repeat strtemp:=Concat(s,strtemp); until Length(strtemp)=8 ; result:=(strtemp); end; function TForm1.int2hex(int:integer):string; begin result:=inttohex(int,2); end; function TForm1.poteN(liczba:int64;do_potegi:integer=2):int64; begin case do_potegi of 0:result:=1; 1:result:=liczba; else PoteN:=liczba*PoteN(liczba,do_potegi-1); end; end; function TForm1.bin2int(binary:string) :int64; var I,j:integer;wynik:Int64; begin wynik:=0; j:=0; for i:=length(binary) downto 1 do begin wynik:=wynik+((strtoint((binary)[i])*( poten(2,j)))); inc(j); end; result:=wynik; end; function int2hex(int:integer):string; begin result:=inttohex(int,2); end; procedure TForm1.Button2Click(Sender: TObject); begin Read_Comm(hCommDev, SizeOf(Buffer_I)); RichEdit1.Text := Buffer_I; end; end.
Wiem, że po RS-sie wędruje ramka, ponieważ podłączyłem oscyloskop i widziałem ją. Dlaczego jednak sterownik wciąż mnie nie rozumie? Dane w ramce są takie same jakie
wysyła MODBUS TESTER i ten otrzymuje odpowiedź.

Handshakingi chyba nie sa potrzebne ale nie jestem pewien.
Ja to muszę jeszcze binarnie zapisać, ale jak to wtedy do bufora wrzucić i przesłać ?
Ostatnio zmieniony 4 maja 2007, o 13:05 przez tobiasz0606, łącznie zmieniany 1 raz.

Wróć do „PLD/FPGA i inne zagadnienia techniki cyfrowej”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 3 gości