Po ogromnych problemach jakie miałem z komunikacją poprzez port COM (RS-232) z pewnym urządzeniem pomiarowym postanowiłem napisać krótki poradnik o tym jak komunikować się poprzez ten port. Niestety wiele poradników w sieci zamydla niesamowicie tą komunikację, a nawet jak się później okazuje, w ogóle nie działa.
Językiem programowania jaki używałem był klasyczny C (może być i C++ ), zaś środowiskiem WINAPI (czyli najbardziej klasyczne podejście do zagadnienia .
No to zaczynamy. Najpierw oczywiście podeklarujmy zmienne potrzebne nam w programie.
static DCB dcb;
static HANDLE hNumPort;
static char lpBuffor_read[33], lpBuffor_write[33];
static DWORD RS_ile;
Mamy zmienne, co one będą trzymać:
dcb - struktura DCB (Device Control Block) definiująca rodzaj połączenia
hNumPort - uchwyt do otwartego portu
lpBuffor_read, lpBuffor_write - tablice z danymi odbieranymi oraz wysyłanymi
RS_ile - zmienna trzymająca informację ile czego wysyłamy lub czytamy
Mając zdefiniowane zmienne zaczynamy zabawę z otwieraniem portu RS 232 . Najpierw otwieramy klasycznie urządzenie jako COM, tak samo jakby to by plik.
hNumPort = CreateFile("COM1", GENERIC_WRITE |
GENERIC_READ, 0, NULL,
OPEN_EXISTING, 0, NULL);
W wyniku funkcji CreateFile otrzymamy uchwyt do pliku, a raczej urządzenie do którego możemy zarówno pisać jak i odczytywać . W naszym przypadku jest to port COM1 lub inaczej COM A.
Mając uchwyt musimy powiedzieć z jakimi parametrami będzie pracował nasz port . Do tego służy struktura DCB. Przykład jej inicjacji.
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = CBR_9600;
dcb.fParity = TRUE;
dcb.Parity = EVENPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.ByteSize = 7;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDsrSensitivity = FALSE;
dcb.fAbortOnError = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
W zasadzie nas obchodzą najbardziej początkowe linijki, pozwoliłem sobie je skomentować.
dcb.BaudRate = CBR_9600; // prędkość transmisji, CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600
dcb.fParity = TRUE; //TRUE jeśli ustawiamy kolejną opcję dla sprawdzenia parzystości
dcb.Parity = EVENPARITY; //Ustawienie parzystości: EVENPARITY (parzysta), MARKPARITY (bit parzystości 1), NOPARITY (bez bitu parzystości), ODDPARITY (nieparzysta)
dcb.StopBits = ONESTOPBIT; // Ustawienie bitu stopu: ONESTOPBIT (jeden bit), TWOSTOPBITS (dwa bity)
dcb.ByteSize = 7; //Liczba bitów wysyłanych/odebranych danych (UWAGA wartościod 5 do 8)
dcb.fDtrControl = DTR_CONTROL_ENABLE; //Kontrola linii DTR: DTR_CONTROL_DISABLE (sygnał nieaktywny), DTR_CONTROL_ENABLE (sygnał aktywny), DTR_CONTROL_HANDSHAKE
dcb.fRtsControl = RTS_CONTROL_ENABLE; //Kontrola linii RTS: RTS_CONTROL_DISABLE (sygnał nieaktywny), RTS_CONTROL_ENABLE (sygnał aktywny), RTS_CONTROL_HANDSHAKE
Te wartości poznasz dzięki instrukcji urządzenia, z którym się komunikujesz. Czasami widnieje inny zapis tego, np "COM1,9600,e,7,1". Tutaj "e" oznacza rodzaj kontroli parzystości EVENPARITY, potem liczba bitów do przesyłania danych i na końcu bit parzystości.
Skoro mamy już zainicjowaną DCB połączmy się z naszym portem. Służy do tego funkcja
SetCommState(hNumPort, &dcb);
Zwraca ona prawdę lub fałsz w zależności od tego czy połączenie nastąpiło czy nie. Zawszy warunkiem IF sprawdzaj czy możesz pisać/czytać czy też nie.
W tej chwili możemy swobodnie pisać i czytać z portu.
Dajmy na to, że chcemy wysłąć komendę, użyjmy do tego komendy WriteFile, tak jakbyśmy pisali do pliku.
strcpy(lpBuffor_write, "RES0070002");// Niech to będzie przykładowe polecenie dla urządzenia
WriteFile(hNumPort, lpBuffor_write, strlen(lpBuffor_write), &RS_ile, 0);
Jak wysyłamy dane to zawsze można je odczytać. Czytanie to klasyczny ReadFile. Odczytajmy sobie 15 znaków
ReadFile(hNumPort, lpBuffor_read, 15, &RS_ile, 0);
Dane można czytać w pętli, interpretować, przeliczać. Ja osobiście polecam w przypadku WINAPI używania Timera systemowego i czytać tylko w trakcie tyknięć zegara. A zatem w przypadku przechwycenia komunikatu WM_TIMER odczytać daną i jak można nawet zatrzymać urządzenie z którego czytamy. Przetworzyć wszystko i na nowo wysłać prośbę o kolejną daną w momencie nowego taktu TIMERa. Ba, można przecież zawsze przy użyciu funkcji KillTimer oraz SetTimer zatrzymywać sobie zegar, przetwarzać złożone operacje a na końcu na nowo uruchamiać zegar.
Na koniec jedna uwaga, zawsze zamykajmy to co otworzyliśmy. A zatem w naszym przypadku mamy otwarty uchwyt do naszego portu. Trzeba go koniecznie zamknąć.
CloseHandle(hNumPort);
W razie jakichkolwiek problemów zapraszam na
FORUM. Z chęcią pomożemy.