CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - BCB MODBUS RTU Wysyłanie i odbieranie RS485

BCB MODBUS RTU Wysyłanie i odbieranie RS485

dział ogólny

BCB MODBUS RTU Wysyłanie i odbieranie RS485

Nowy postprzez piotr.kwlk » czwartek, 27 marca 2014, 07:59

Mam problem dotyczący wysyłania i odbierania wartości po przez RS485 zgodnie z Modbus RTU. Wcześniej opracowałem odczyt ASCI i działało za pomocą funkcji
KOD cpp:     UKRYJ  
ReadFile(hCommDev, &Buffer_I[0], nNumberOfBytesToRead, lpNumberOfBytesRead, NULL);

Przesyłając wartości z programu testującego zgodnie z Modbus RTU otrzymuje jakieś krzaczki. Szukam funkcji która zwróci mi wartość Hex lub w bajtach, w końcu port przesyła wartości w bajtach. Niestety nie wiem jak odczytać te wartości.
Przykładowy kod:
KOD cpp:     UKRYJ  
    #define cbOutQueue 16         //rozmiar bufora danych wyjściowych
    #define cbInQueue  16         //rozmiar bufora danych wejściowych


    char    Buffer_O[cbOutQueue]; // bufor danych wyjściowych
    char    Buffer_I[cbInQueue];  // bufor danych wejściowych
    DWORD   Buffer;
    DWORD   Number_Bytes_Read;    // Number bytes to read -
                                  // liczba bajtów do czytania
    HANDLE  hCommDev;             // identyfikator portu
    LPCTSTR lpFileName;           // wskaźnik do nazwy portu
    DCB     dcb;                  // struktura kontroli portu szeregowego
    DWORD   fdwEvtMask;           // informacja o aktualnym stanie
                                  // transmisji
    COMSTAT Stat;                 // dodatkowa informacja o zasobach
                                  // portu
    DWORD   Errorss;               // reprezentuje typ ewentualnego błędu
//---------------------------
int Close_Comm(HANDLE hCommDev)
{
    CloseHandle(hCommDev);
    return TRUE;
}
//---------------------------

int Read_Comm(HANDLE hCommDev, LPDWORD lpNumberOfBytesRead, DWORD Buf_Size)
{
    DWORD nNumberOfBytesToRead;

    ClearCommError(hCommDev, &Errorss ,&Stat);

    if (Stat.cbInQue > 0){
        if (Stat.cbInQue > Buf_Size)
            nNumberOfBytesToRead = Buf_Size;
          else
            nNumberOfBytesToRead = Stat.cbInQue;

        ReadFile(hCommDev, &Buffer_I[0], nNumberOfBytesToRead,
                 lpNumberOfBytesRead, NULL);
      }
      else
        *lpNumberOfBytesRead = 0;
    return TRUE;
}
//--------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  memset(Buffer_I, 0, cbInQueue);

  PurgeComm(hCommDev, PURGE_TXABORT);
  Read_Comm(hCommDev, &Number_Bytes_Read, sizeof(Buffer_I));
 

  Label1->Caption = (String)(Buffer_I);
 
  if(Label1->Caption.Length()){
    Memo1->Lines->Add(Label1->Caption);
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

  lpFileName = "COM1";
  hCommDev = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE,
                          0, NULL, OPEN_EXISTING, 0, NULL);

  if (hCommDev != INVALID_HANDLE_VALUE)   // sprawdza, czy port jest
                                            // otwarty prawidłowo
  {
    SetupComm(hCommDev, cbInQueue, cbOutQueue);

    dcb.DCBlength = sizeof(dcb);
    GetCommState(hCommDev, &dcb);

    dcb.BaudRate = CBR_9600;

    //dcb.Parity = ODDPARITY;            // ustawienie parzystości
    dcb.Parity = NOPARITY;            // ustawienie parzystości
    dcb.StopBits = ONESTOPBIT;         // bity stopu
    dcb.ByteSize = 8;                  // bity danych

    //-przykładowe ustawienia flag sterujących DCB-
    dcb.fParity = TRUE;               // sprawdzanie parzystości
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fOutxCtsFlow = FALSE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fAbortOnError = FALSE;
    dcb.fOutX = FALSE;
    dcb.fInX = FALSE;
    dcb.fErrorChar = FALSE;
    dcb.fNull = FALSE;

    SetCommState(hCommDev, &dcb);

    GetCommMask(hCommDev, &fdwEvtMask);
    SetCommMask(hCommDev, EV_TXEMPTY);

    Button1->Enabled=false;
    Button2->Enabled=true;

    Timer1->Enabled = true;

  }
  else
  {
    switch ((int)hCommDev){
      case IE_BADID:
        MessageBox(NULL, "Niewłaściwa nazwa portu lub port jest"
                    " aktywny.", "Błąd", MB_OK);
        break;
    };
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  Timer1->Enabled = false;
  Close_Comm(hCommDev);
  Button1->Enabled=true;
  Button2->Enabled=false;
}
//---------------------------------------------------------------------------
 

Od razu poproszę o informację jak wysyłać wartości.
Sprawa dla mnie bardzo ważna i pilna.
Piotr
Avatar użytkownika
piotr.kwlk
Bladawiec
Bladawiec
 
Posty: 25
Dołączył(a): środa, 9 marca 2011, 17:34
Podziękował : 0
Otrzymał podziękowań: 0
System operacyjny: Windows XP
Kompilator: Builder 6
Gadu Gadu: 0
    Windows 7Chrome

Re: BCB MODBUS RTU Wysyłanie i odbieranie RS485

Nowy postprzez Cyfrowy Baron » sobota, 29 marca 2014, 10:36

Używaj dalej ReadFile, ale zamiast char użyj BYTE. Funkcja ReadFile oczekuje następujących argumentów:

KOD cpp:     UKRYJ  
ReadFile( void *hFile, void* lpBuffer,unsigned long nNumberOfByteToRead, unsigned long lpNUmberOfBytesREad, _OVERLAPPED * lpOverlapped )


Jak widać można pobierać bajty.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows 7Firefox

Re: BCB MODBUS RTU Wysyłanie i odbieranie RS485

Nowy postprzez piotr.kwlk » środa, 2 kwietnia 2014, 10:18

Potrafię już odczytywać wartości HEX i DEC każdego bajtu. Jednak według Modbus RTU pierwszy bajt to adres urządzenia, drugi bajt to funkcja, następne mogą zawierać n*bajtów wartość. Teraz się głowie jak połączyć np 2 czy tez 4 bajty aby uzyskać wartość int. Czy jest jakaś funkcja w c++ która to zrobi bo jedynie mogę dodawać odejmować itp. ale nie wiem jak połączyć bajty i odczytać jako pojedyncza wartość.
Próbowałem odczytać w pętli ReadFile podając rożne zakres bajtów. Odczytując po kolei wartości przesuwa wskaźnik o jeden bajt niezależnie czy czytam jeden czy więcej. W rezultacie nie uzyskuje wartości oczekiwanej, a nielogiczne jest wywoływanie Read_Comm3 aby przeskoczyć o ilość bajtów. Przypuszczam że można by ustawić w zmiennej &Number_Bytes_Read, ale dokładnie nie wiem czy na pewno. Liczę na pomoc.

KOD cpp:     UKRYJ  
int t_buf[]={1,1,4,2};
  PurgeComm(hCommDev, PURGE_TXABORT);
  AnsiString war="";
  for(int i=0; i<cbInQueue; i++){
    Read_Comm3(hCommDev, &Number_Bytes_Read, t_buf[i]);
    war+=" "+IntToStr(BufferI);
    BufferI=0;
  }
  Memo1->Lines->Add(war);


gdzie funkcja Read_Comm3.
KOD cpp:     UKRYJ  
int Read_Comm3(HANDLE hCommDev, LPDWORD lpNumberOfBytesRead, DWORD Buf_Size)
{
    DWORD nNumberOfBytesToRead;

    ClearCommError(hCommDev, &Errorss ,&Stat);

    if (Stat.cbInQue > 0){
        if (Stat.cbInQue > Buf_Size)
            nNumberOfBytesToRead = Buf_Size;
          else
            nNumberOfBytesToRead = Stat.cbInQue;

        ReadFile(hCommDev, &BufferI, nNumberOfBytesToRead,
                 lpNumberOfBytesRead, NULL);
      }
      else
        *lpNumberOfBytesRead = 0;
    return TRUE;
}

Zaznaczę że BufferI jest to zmienna typu int globalna.
Piotr
Avatar użytkownika
piotr.kwlk
Bladawiec
Bladawiec
 
Posty: 25
Dołączył(a): środa, 9 marca 2011, 17:34
Podziękował : 0
Otrzymał podziękowań: 0
System operacyjny: Windows XP
Kompilator: Builder 6
Gadu Gadu: 0
    Windows 7Chrome

Re: BCB MODBUS RTU Wysyłanie i odbieranie RS485

Nowy postprzez piotr.kwlk » środa, 2 kwietnia 2014, 12:07

Chyba mi się udało. Zaznaczę, że Buffer zadeklarowałem globalnie BYTE Buffer[cbInQueue];

KOD cpp:     UKRYJ  
  memset(Buffer, 0, cbInQueue);

  int tab_i[4],t_buf[]={1,1,4,2};

  PurgeComm(hCommDev, PURGE_TXABORT);
  Read_Comm2(hCommDev, &Number_Bytes_Read, sizeof(Buffer));
  AnsiString war="",war_h="",war_i="",war_ih="";

 for(int i=0; i<cbInQueue; i++){
    war+=" "+IntToStr((int)(Buffer[i]));
    war_h+=" "+IntToHex((int)(Buffer[i]),2);
  }
  int j=0;
  for(int i=0; i<cbInQueue;){
    tab_i[j]=(int)(Buffer[i]);
    if(t_buf[j]==4){
        for(int y=0;y<t_buf[j];y++){
            tab_i[j]=(tab_i[j]<<8)|(int)(Buffer[i]);
            i++;
        }
    }
    else{
        i++;
    }
    war_i+=" "+IntToStr(tab_i[j]);
    war_ih+=" "+IntToHex(tab_i[j],2);
    j++;
  }

  if(Buffer[0]>0){
    Memo1->Lines->Add("int/bit: "+war);
    Memo1->Lines->Add("hex/bit: "+war_h);
    Memo1->Lines->Add("--------------------------");
    Memo1->Lines->Add("int/zmienna: "+war_i);
    Memo1->Lines->Add("hex/zmienna: "+war_ih);
    Memo1->Lines->Add("===========================");
  }

Po prostu pisząc przesuwam bity i wykonuje sumę bitową jak widać w drugiej pętli. Proszę o opinię, a może ktoś zna inny sposób ?
Piotr
Avatar użytkownika
piotr.kwlk
Bladawiec
Bladawiec
 
Posty: 25
Dołączył(a): środa, 9 marca 2011, 17:34
Podziękował : 0
Otrzymał podziękowań: 0
System operacyjny: Windows XP
Kompilator: Builder 6
Gadu Gadu: 0
    Windows 7Chrome


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 59 gości

cron