Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Marek 14. 02. 2014, 17:40:26
-
Ahojte.
Zopár hodín sa trápim s tým, ako čítať DATA zo sériového portu (rs232). Program píšem v C++ (Code::Blocks + MINGW), štandardné WINAPI GUI.
cez case WM_COMMAND: -> case(param) -> Otváram Port ....CreateFile.... co funguje
otvorenie portu, a zápis fungujú v pohode.
Horšie je to s príjmom Dát z RS232. Nedarí sa mi to spraviť tak, aby som mohol prijatý text vykresliť v GUI (label).
Neviem nájsť žiadny EVENT, respektíve prerušenie po prijatí znaku "EV_RXCHAR". Môžete mi poradiť, ako spraviť nepretržité čítanie dát, event, alebo aj pooling, tak aby som to vedel zapracovať do GUI (label)?
Štandardné GUI
#include <windows.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "CodeBlocksWindowsApp";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Code::Blocks Template Windows App", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
544, /* The programs width */
375, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
MSDN o Seriovej komunikacii
http://msdn.microsoft.com/en-us/library/ms810467.aspx
Dakujem M.
-
1) CreateFile(,OVERLAPPED)
2) ReadFile(,OVERLAPPED)
3) misto GetMessage() pouzit MsgWaitForMultipleObjects() a v Count=1, Handles[0] bude EVENT, ktery byl pouzity pro Overlapped
MsgWaitForMultipleObjects() vrati:
WAIT_OBJECT_0 -> data v bufferu
WAIT_OBJECT_0+1 -> zprava pro okno
Takze misto:
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
bude:
HANDLE File;
HANDLE Event;
OVERLAPPED Overlapped;
File=CreateFile(...,FILE_FLAG_OVERLAPPED,...);
Event=CreateEvent(....);
Overlapped.hEvent=Event;
// start new async read request
ReadFile(File,....,&Overlapped);
while(1){
DWORD Result;
Result=MsgWaitForMultipleObjects(1,&Event,FALSE,INFINITE,
QS_ALLEVENTS | QS_ALLPOSTMESSAGE | QS_SENDMESSAGE);
if (Result==WAIT_OBJECT_0){
// get data ..
GetOverlappedResult(File,&Overlapped,....);
// copy data from Overlapped somewhere
// start new async read request
ReadFile(File,....,&Overlapped);
}
if (Result==WAIT_OBJECT_0+1){
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)){
if (Msg.message == WM_QUIT){
break;
}
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
if (Msg.message==WM_QUIT){
break;
}
}
}
// must cancel all pending requests !!!!
CancelIo(File);
Toz tak
-
Jo, a mimochodem, pokud bys nemel ten port otevrity porad, lze do MsgWaitFor...() dat i prvni parametr (pocet) jako 0 :-)
-
Záleží, jaký je to řízení.
V synchronním řízení nejdřív zapíšu a pak přečtu a dokud znova nezapíšu, nepotřebuju číst... třeba jako kdyby to byl http request, tak podobně... pak je to snadné, stačí jen volat WriteFile, ReadFile.
V asynchronním řízení, tedy pokud potřebuju trvale monitorovat port čtením a zároveň občas do něj i zapsat, pak doporučuju jednoznačně
- Otevřít to s příznakem OVERLAPPED a číst tímto způsobem
- a zároveň číst to v jiném vlákně
Protože při volání ReadFile se zamyká nějaký zámek na tom handle, nedá se volat WriteFile, když jiné vlákno visí v ReadFile. Proto je nutné mít OVERLAPPED, který způsobí, že vlákno nečeká v systémovém volání, ale na nějakém eventu.
Ono to sice jde číst v UI, ale z hlediska návrhu aplikace to není zrovna čisté řešení. Už jen proto, že musíte modifikovat smyčku pro čtení zpráv, což je zásah mající globální dopad na řízení běhu aplikace
Další možností je použít APC a v obsluze APC si poslat do okna zprávu pomocí PostMessage, která vydráždí UI vlákno k tomu, aby si načetla přijatá data.
-
Ahojte, ďakujem za pomoc, v pláne je spraviť GUI,v GUI budu zobrazene hodnoty prijaté z uartu(virtual USB/COM), občasne sa spravi write(na seriovy port), ale v drtivej vacsine to bude prijimanie.
Prepísal som to, ako mi poradil Ogar, GUI sa vykresli, DRAW funguje. Po prvom write, a následnom receive(read) sa však GUI kusne, v debugu skace stale na (Result==WAIT_OBJECT_0), aj ked ziadne data uz niesu posielane na seriovy port, (Readfile) nema co citat, nic neprichadza. Momentalne mam "premosteny" RX/TX na virtual USB/COM , teda poslem znak, hned ho aj primem.
s WINAPI robim prvykrat, som zvyknuty na procesory, nastavim interrupt flag pre RX, pride event, spracujem, a pokracujem dalej.
:Ogar: Port mam stale otvoreny, writefile(10%), readfile(90%) z celkoveho casu.
:Ondřej Novák: Ďakujem, skusim pozriet ten APC, ale ak by ste mal príklad, resp vhodnu literaturu, budem vdacny.
s pozdravom M.
RS232
#include <windows.h>
#include "RS232.h"
extern HANDLE hSerial; // Global Handle
bool OpenSerialPort(HWND hwnd,LPCSTR _PortName)
{
hSerial = CreateFile(_PortName, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); // OPEN PORT
if(hSerial == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_NOT_FOUND) MessageBox(hwnd, "Port not avalible", "ERROR", MB_OK);
return false;
}
DCB SPort = {0}; //
SPort.DCBlength = sizeof(DCB);
if(GetCommState(hSerial, & SPort))
{
SPort.BaudRate=CBR_115200; // SerilPort Baudrate
SPort.ByteSize=8; // Byte size 8
SPort.StopBits=ONESTOPBIT;
SPort.Parity=NOPARITY;
}
else
{
MessageBox(hwnd, "COM_Error_103", "ERROR", MB_OK);
return false;
}
if (!SetCommMask(hSerial, EV_RXCHAR)) MessageBox(hwnd, "COM_Error_103", "ERROR", MB_OK);
return true;
}
//----------------------------------
//----------------------------------
bool CloseSerialPort(void)
{
if(CloseHandle(hSerial)) return 1; // Closed OK - return 1
else return 0; // Closed FX - return 0 -> Error
}
//----------------------------------
//----------------------------------
bool WriteABuffer(HWND hwnd, BYTE * lpBuf, DWORD dwToWrite)
{
OVERLAPPED osWrite = {0};
DWORD dwWritten;
DWORD dwRes;
BOOL fRes;
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!WriteFile(hSerial,(LPCVOID) lpBuf, dwToWrite, &dwWritten, &osWrite))
{
if (GetLastError() != ERROR_IO_PENDING) fRes = FALSE;
else
dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch(dwRes)
{
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hSerial, &osWrite, &dwWritten, FALSE)) fRes = FALSE;
else fRes = TRUE;
default: fRes = FALSE;
}
}
else
{
MessageBox(hwnd, "OK", "OK", MB_OK);
fRes = true;
}
CloseHandle(osWrite.hEvent);
return fRes;
}
Main.cpp
Event = CreateEvent(NULL, TRUE, FALSE, NULL);
Overlapped.hEvent = Event;
char lpBuf;
DWORD value;
DWORD dwCommEvent;
while(1)
{
Result=MsgWaitForMultipleObjects(1,&Event,FALSE,INFINITE, QS_ALLEVENTS | QS_ALLPOSTMESSAGE | QS_SENDMESSAGE);
if (Result==WAIT_OBJECT_0)
{
GetOverlappedResult(hSerial, &Overlapped, &value , FALSE);
if(WaitCommEvent(hSerial, &dwCommEvent, NULL))
{
do {
ReadFile(hSerial, &chRead, 1, &dwRead, &Overlapped);
MessageBox(hwnd, "ok", "oko", MB_OK);
}while (dwRead);
}
}
if (Result==WAIT_OBJECT_0+1){
{
while (PeekMessage(&messages, NULL, 0, 0, PM_REMOVE))
{
if (messages.message == WM_QUIT){
break;
}
TranslateMessage(&messages);
DispatchMessage(&messages);
}
}
}
}
....
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
case ID_BUTTON_CONNECT:
OpenSerialPort(hwnd,"COM3");
break;
case ID_BUTTON_SEND:
WriteABuffer(hwnd,&a,1);
if(bSet == true) ReadFile(hSerial, &chRead, 1, &dwRead, &Overlapped);
bSet = false;
break;
...
}
-
Ach jo, holt (slovy klasika) 'to je tak, kdyz nekdo necte a rozmrazi' :-(
MSDN dokumentace na webu
HANDLE WINAPI CreateEvent(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset,
_In_ BOOL bInitialState,
_In_opt_ LPCTSTR lpName
);
Druhy parametr CreateEvent() je bManualReset, coz znamena, ze si ten Event MUSIS VYCISTIT SAM funkci ResetEvent() !!!!
A nebo samozrejmne ho nastavit na FALSE ...
-
A jak uz psal Ondrej Novak, cistejsi je opravdu to udelat ve dvou vlaknech:
a) prvni cte, zapisuje, a pres nejaky synchro prostredek dava vedet tomu druhemu
b) druhe vlakno sa jenom a pouze stara o GUI.
Ale holt, jaka otazka, takova odpoved :-)