Odbiór danych z RS232 poprzez ComPort

Odbiór danych z RS232 poprzez ComPort
ZT
  • Rejestracja:około 11 lat
  • Ostatnio:około 8 lat
  • Postów:18
0

Witam.
Mam problem z odbiorem danych z RS232 za pomocą komponentu ComPort. Za każdym razem, gdy do urządzenia wyślemy jakąkolwiek komendę zwraca ono cyfrę 2. Na próbę napisałem taki prosty program. Na początku wysyłamy pierwszą komendę a następną (z resztą cały czas to samo) wysyłamy dopiero, gdy urządzenia odeśle nam znak 2 - i tak robimy 10 razy. Odbiór zrealizowałem w zdarzeniu ComPort1RxChar
Cały program wygląda tak:

Kopiuj
 unit Test;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, CPort, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ComPort1: TComPort;
    Button2: TButton;
    Button3: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure ComPort1RxChar(Sender: TObject; Count: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
    stop : byte;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
begin
  if ComPort1.Connected then
  begin
    ComPort1.Close;
    button2.Caption:='Połącz';
  end else begin
    ComPort1.Open;
    button2.Caption:='Rozłącz';

  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
ComPort1.ShowSetupDialog;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
st : string;
licznik : integer;
begin
for licznik := 1 to 10 do
begin
if licznik > 1 then
while stop <> 1 do
begin
end;
stop := 0;
st:=#27 + '008couTEST'  + #27 ;
if ComPort1.Connected then
ComPort1.WriteStr(st);
end;
end ;



procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
int: byte;
begin
  ComPort1.Read(int, count);
   if int = 2 then
   stop:=1;
end;


end.

Pętla While jest wprowadzona tutaj do testu. Jednak program ten, nie działa. Raz wyśle to co powinien, potem się zawiesza, a teoretycznie nie powinien, bo zmienna Stop powinna przyjąć wartość 1. Robiłem już testy, które polegały na dodaniu w zdarzeniu ComPort1RxChar instrukcji która dane z RS-a wpisywała do memo. Podczas gdy powinny być one wpisane po każdym przejściu pętli for to w rzeczywistości następowało to naraz dopiero po ostatnim przejściu pętli. Wygląda to tak jakby zdarzenie ComPort1RxChar nie było wywoływane za każdym razem gdy program odbierze jakiś znak. Co może być tego przyczyną?

olesio
  • Rejestracja:prawie 17 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Szczecin
  • Postów:4191
0

Nie mam za dużego doświadcxenia z TComPort. Poza tym jestem po urodzinach kolegi i znacznej ilości Tyskich. Ale jak robiłem kiedyś program z tym komponentem dawno temu, to posłużyłem się zdarzeniem OnRxBuf. I nie było problemów. Przykładowy kod poniżej.

Ale pewnie ktoś jeszcze na trzeźwo coś tutaj doradzi być może lepszego. Tylko zachodzę w głowę, że do tej pory nikt jednak nie raczył odpisać. Może jednak to, że mamy weekend i mistrzostwa w piłkę kopaną w Brazylii - są przyczyną.

Kopiuj
procedure TMainForm.VictronComPortRxBuf(Sender : TObject; const Buffer; Count : Integer);
var
  Edt : TEdit;
  S, LabStr, ValStr : string;
  Cnt, I, J, Idx, X, V : integer;
begin
  if VictronSL = nil then
  begin
    VictronSL := TStringList.Create;
  end;
  SetLength(S, Count);
  Move(Buffer, S[1], Count);
  if VictronSL <> nil then
  begin
    if Copy(S, 1, Length(CRLF)) = CRLF then
    begin
      VictronSL.Clear;
      VictronWholeStr := '';
    end;
    VictronWholeStr := VictronWholeStr + S;
    VictronSL.Text := VictronWholeStr;
    Cnt := 0;
    for I := 0 to VictronSL.Count - 1 do
    begin
      X := Pos(Victron_Label_separator, VictronSL[I]);
      if X > 0 then
      begin
        LabStr := Copy(VictronSL[I], 1, X - 1);
        ValStr := Copy(VictronSL[I], X + Length(Victron_Label_separator), MaxInt);
        Idx := VictronLabToIndex(LabStr);
        if Idx > 0 then
        begin
          if TryStrToInt(ValStr, V) then
          begin
            Edt := TEdit(Self.FindComponent(Victron_Edits_Base_Name + IntToStr(Idx)));
            if Edt <> nil then
            begin
              Cnt := Cnt + 1;
              case Idx of
                1, 2, 3, 4, 7, 8, 9, 12, 13, 14 :
                  begin
                    Edt.Text := FormatFloat('0.000', V * 0.001);
                  end;
                5 :
                  begin
                    Edt.Text := FormatFloat('0.0%', V * 0.1);
                  end;
                6 :
                  begin
                    Edt.Text := FormatFloat('0.00h', V * 0.01);
                  end;
                15 :
                  begin
                    Edt.Text := FormatFloat('0.000', V / 3600);
                  end;
              else
                begin
                  Edt.Text := ValStr;
                end;
              end;
              if Cnt = Max_Victron_Edits_Count - Victron_Empty_Edits_Count then
              begin
                VictronCsvDataStr := '';
                for J := 1 to Max_Victron_Edits_Count do
                begin
                  Edt := TEdit(Self.FindComponent(Victron_Edits_Base_Name + IntToStr(J)));
                  if Edt <> nil then
                  begin
                    if Edt.Text <> '' then
                    begin
                      VictronCsvDataStr := VictronCsvDataStr + Edt.Text + Default_Separator;
                    end;
                  end;
                end;
                if VictronCsvDataStr <> '' then
                begin
                  if VictronCsvDataStr[Length(VictronCsvDataStr)] = Default_Separator then
                  begin
                    Delete(VictronCsvDataStr, Length(VictronCsvDataStr), 1);
                    VictronCsvDataStr := VictronCsvDataStr + CRLF;
                  end;
                end;
                VictronTimer.Enabled := True;
                if VictronTimer.Tag = 0 then
                begin
                  VictronTimer.Tag := 1;
                  if @VictronTimer.OnTimer <> nil then
                  begin
                    VictronTimer.OnTimer(VictronTimer);
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

P.S.: Może powyższe jest mocno przekombinowane, ale w projekcie który realizowałem sprawdziło się jak należy i trzeba było kombinować w ten sposób by urządzenie docelowe ogarnać tak jak sobie ktoś założył.


Pozdrawiam.
MA
  • Rejestracja:ponad 16 lat
  • Ostatnio:25 dni
0
Znawca tematu napisał(a):

Witam.
Mam problem z odbiorem danych z RS232 za pomocą komponentu ComPort. Za każdym razem, gdy do urządzenia wyślemy jakąkolwiek komendę zwraca ono cyfrę 2. Na próbę napisałem taki prosty program. Na początku wysyłamy pierwszą komendę a następną (z resztą cały czas to samo) wysyłamy dopiero, gdy urządzenia odeśle nam znak 2 - i tak robimy 10 razy. Odbiór zrealizowałem w zdarzeniu ComPort1RxChar

Widzę drobną, choć istotną nieścisłość: urządzenia zwraca znak '2' jako ASCII, czy binarnie? Twój kod obsługuje odbiór 2-ki binarnie. Co do kod, to podstawowym błędem jest wysyłanie kolejnych komend w pętli While w procedurze Button3Click. Nie zapewni to urządzeniu zareagować (przetworzyć, odpowiedzieć) na ostatnio wysyłaną komendę w pętli While w procedurze Button3Click, a już dostaje następną. Powinieneś kolejne komendy wysyłać dopiero po odebraniu odpowiedzi na ostatnio wysyłaną komendę w procedurze ComPort1RxChar, np.

Kopiuj
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
int: byte;
begin
  ComPort1.Read(int, count);
   if jakis_warunek_w_odpowiedzi then
   begin
     //wysyłanie dalszych komend
   end
  else
  begin
    //koniec wysyłania komend
  end;
end; 

Sorki, piszę bardzo skrótowo, ale bardzo się spieszę ;)

ZT
  • Rejestracja:około 11 lat
  • Ostatnio:około 8 lat
  • Postów:18
0
marogo napisał(a):

Widzę drobną, choć istotną nieścisłość: urządzenia zwraca znak '2' jako ASCII, czy binarnie? Twój kod obsługuje odbiór 2-ki binarnie. Co do kod, to podstawowym błędem jest wysyłanie kolejnych komend w pętli While w procedurze Button3Click. Nie zapewni to urządzeniu zareagować (przetworzyć, odpowiedzieć) na ostatnio wysyłaną komendę w pętli While w procedurze Button3Click, a już dostaje następną. Powinieneś kolejne komendy wysyłać dopiero po odebraniu odpowiedzi na ostatnio wysyłaną komendę w procedurze ComPort1RxChar, np.

Urządzenie wysyła dwóję oczywiście binarnie. Pętla While właśnie została tutaj wykorzystana (tylko tak dla przykładu) żeby czekać, aż urządzenie zwróci znak gotowości. Zmienna Stop ma wartość 0 więc warunek stop<>1 jest spełniony i program stoi w miejscu(wykonuje pustą pętlę). Gdy Zostanie odebrany przez Rs232 znak 2 to zmienna stop przyjmuje wartość 1 i warunek nie jest spełniony, a więc program leci dalej. Wysyłanie jest realizowane 10 razy w pętli for. Jak już pisałem ta pętla while została tutaj wykorzystana żeby tak szybko przedstawić o co mi chodzi. Docelowo wykonam to zapewne inaczej.
Zaraz spróbuję coś z kodem kolegi olesio. Mam jednak wrażenie, że jest jakiś problem z szybkim odbieraniem i wysyłaniem przez RS. Mianowicie gdy wysyłamy jakąś komendę to informację zwrotną dostajemy po kilku milisekundach. Próbowałem zrealizować odbiór w osobnym wątku i gdy program miał tylko odebrać jakąś informację było ok. Gdy jednak miał już coś wysłać przez RS-a i za chwilę odebrać(w osobnym wątku) odpowiedź to nie chciało to już działać.

edytowany 1x, ostatnio: Znawca tematu
MA
  • Rejestracja:ponad 16 lat
  • Ostatnio:25 dni
0
Znawca tematu napisał(a):

Pętla While jest wprowadzona tutaj do testu. Jednak program ten, nie działa. Raz wyśle to co powinien, potem się zawiesza, a teoretycznie nie powinien, bo zmienna Stop powinna przyjąć wartość 1.

Po kliknięciu przycisku Button3 program się zawiesza, bo wpada w pustą pętlę While (co zapewne niepotrzebnie bardzo obciąża procesor), co dodatkowo uniemożliwia zakończenie procedury Button1Click, przez co program nie może wykonać procedury odbioru danych z urządzenia ComPort1RxChar. Gdyby chociaż w pętli While było Application.ProcessMessages, pozwoliłoby to przetworzyć kolejkę komunikatów, m.in. zdarzenie ComPort1RxChar, a tak, program praktycznie wisi w pętli While.
Tak jak napisałem wcześniej, kolejne wysyłanie komend powinno odbywać się dopiero po odbiorze pierwszej komendy, ale w procedurze ComPort1RxChar (jako reakcja na odpowiedź urządzenia). Nie ma potrzeby stosowania dodatkowego wątku, bo jak być sobie popatrzył w kod źródłowy ComPort-u, to każde zdarzenie w tym komponencie to już jest osobny wątek. Warto też użyć timera odmierzającego timeout odpowiedzi urządzenia, bo jeśli z jakiegoś powodu (np. brak połączenia) urządzenie nie odpowie (np. w ciągu 3 s), to program powinien zgłosić takie zdarzenie.

ZT
  • Rejestracja:około 11 lat
  • Ostatnio:około 8 lat
  • Postów:18
0

W pętli miało się znaleźć jeszcze sleep (xx) żeby trochę ją spowolnić. Źle trochę zrozumiałem zasadę działania zdarzeń. Programy na komputer piszę sporadycznie, więcej piszę na mikroprocesory i tam mam coś takiego jak przerwanie. Ma ono znaczenie nadrzędne nad programem i adres aktualnie wykonywanej procedury jest odkładany na stos, a program "leci" wykonać to co w przerwaniu. Myślałem(teraz już wiem, że błędnie), że tutaj działa to na podobnej zasadzie.
Faktycznie "magiczne" Application.ProcessMessages powoduje, że program działa jak powinien.
Co do timeout-a to zapewne coś takiego będzie. To jest tylko mała część z programu, jaki muszę stworzyć.
Dziękuję Wam za dotychczasową pomoc. Muszę już tylko poskładać to w jakąś sensowną całość bez tej pętli While.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)