Similar with the linux version , this article would demonstrate how to write a simple windows .
I divide the 2 operations : write and read into 2 threads. Of course, one could call ReadFile and WriteFile (those are mapping to read/wrrite function in linux) at the same thread.
Be prudent, if you would like to create more than 2 threads, do not be annoyed with locking/unlocking the threads.
#include <windows.h> #include <stdio.h> #define MAX_STR_LEN (512) #define UART_TRANS_BUFFER (128) #define MILLI_SEC (1) BOOL isLeaveThread; CRITICAL_SECTION UARTThreadCriticalSection; TCHAR *GetLastErrorMessage(DWORD lastError) { static TCHAR errmsg[MAX_STR_LEN]; if (0 == FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, lastError, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT) /*american empire language*/, errmsg, MAX_STR_LEN - 1, NULL)) { /* if we fail, call ourself to find out why and return that error */ return (GetLastErrorMessage(GetLastError())); } return errmsg; }/*GetLastErrorMessage*/ DWORD WINAPI ReadThread(LPVOID lpParameter) { HANDLE hSerial; char readBuffer[UART_TRANS_BUFFER]; hSerial = *((HANDLE*)lpParameter); do { BOOL isSuc; DWORD bytesRead; unsigned int i; ZeroMemory(&readBuffer[0], UART_TRANS_BUFFER); EnterCriticalSection(&UARTThreadCriticalSection); isSuc = ReadFile(hSerial, &readBuffer[0], UART_TRANS_BUFFER, &bytesRead, NULL); if(FALSE == isSuc) { printf("ERROR : %s : %s\n", __FUNCTION__, GetLastErrorMessage( GetLastError()) ); }/*if*/ Sleep(5); if(0 == bytesRead) { printf(" No Ack!\n"); continue; } for(i = 0; i< bytesRead; i++) printf("%c(%d %#x)\t", readBuffer[i], readBuffer[i], readBuffer[i]); LeaveCriticalSection(&UARTThreadCriticalSection); }while(FALSE == isLeaveThread); printf("leaving %s\n", __FUNCTION__); ExitThread((DWORD)0); }/*ConfirmThread*/ DWORD WINAPI WriteThread(LPVOID lpParameter) { HANDLE hSerial; char sendBuffer[UART_TRANS_BUFFER]; DWORD bytesWritten; BOOL isSuc; hSerial = *((HANDLE*)lpParameter); sprintf_s(&sendBuffer[0], UART_TRANS_BUFFER, "hello!"); do { EnterCriticalSection(&UARTThreadCriticalSection); isSuc = WriteFile(hSerial, &sendBuffer[0], strlen(&sendBuffer[0]), &bytesWritten, NULL); if(FALSE == isSuc) { printf("ERROR : %s : %s\n", __FUNCTION__, GetLastErrorMessage( GetLastError()) ); }/*if*/ Sleep(5); LeaveCriticalSection(&UARTThreadCriticalSection); }while(FALSE == isLeaveThread); printf("leaving %s\n", __FUNCTION__); ExitThread((DWORD)0); }/*CommandThread*/ /* for timeoutInterval: 0 : return immediately a value in range of (0 25.5] : timeout mode, the value be in unit of milli-sec otherwise : blockmode */ BOOL SetSerialProperties( HANDLE hSerial, DWORD baudRate, BYTE stopBits, BYTE parity, int timeoutInterval) { DCB dcbSerialParams; COMMTIMEOUTS timeouts; ZeroMemory(&dcbSerialParams, sizeof(DCB)); ZeroMemory(&timeouts, sizeof(COMMTIMEOUTS)); if(FALSE == GetCommState(hSerial, &dcbSerialParams)) { fprintf(stderr, "ERROR : GetCommState , "); return FALSE; }/*if FALSE == isSuc*/ dcbSerialParams.BaudRate = baudRate; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = stopBits; dcbSerialParams.Parity = parity; if(FALSE == SetCommState(hSerial, &dcbSerialParams)) { fprintf(stderr, "Error : setting device parameters , "); return FALSE; }/*if */ if(0 == timeoutInterval) { //return immediately timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; } else if( 0 > timeoutInterval || timeoutInterval > MAXBYTE*100*MILLI_SEC ) /*compatible with linux*/ { //block mode timeouts.ReadIntervalTimeout = 0; timeouts.ReadTotalTimeoutMultiplier = 0 ; /*timeout for per read*/ timeouts.ReadTotalTimeoutConstant = 0 ; } else { // timeout mode timeouts.ReadIntervalTimeout = 1; /* time per byte, milli-sec*/ timeouts.ReadTotalTimeoutMultiplier = 1; /*timeout for per read*/ timeouts.ReadTotalTimeoutConstant = (DWORD)timeoutInterval; /*ertra time for read timeout*/ }/*if timeoutInterval*/ /*WriteTotalTimeoutConstant = WriteTotalTimeoutMultiplier = 0 : write operation is without timeout*/ timeouts.WriteTotalTimeoutConstant = 0; /*timeout for per write*/ timeouts.WriteTotalTimeoutMultiplier = 0; /*extra time for write timeout*/ if(FALSE == SetCommTimeouts(hSerial, &timeouts)) { fprintf(stderr, "error : setting port state , "); return FALSE; }/*if */ return TRUE; }/*SetSerialProperties*/ BOOL CtrlHandler( DWORD fdwCtrlType ) { switch( fdwCtrlType ) { // Handle the CTRL-C signal. case CTRL_C_EVENT: printf( "Ctrl-C event\n\n" ); Beep( 750, 300); isLeaveThread = TRUE; return( TRUE ); #if(0) // CTRL-CLOSE: confirm that the user wants to exit. case CTRL_CLOSE_EVENT: Beep( 600, 200); printf( "Ctrl-Close event\n\n" ); return( TRUE ); // Pass other signals to the next handler. case CTRL_BREAK_EVENT: Beep( 900, 200); printf( "Ctrl-Break event\n\n" ); return FALSE; case CTRL_LOGOFF_EVENT: Beep( 1000, 200); printf( "Ctrl-Logoff event\n\n" ); return FALSE; case CTRL_SHUTDOWN_EVENT: Beep( 750, 500); printf( "Ctrl-Shutdown event\n\n" ); return FALSE; #endif default: break; }/*switch*/ return FALSE; }/*CtrlHandler*/ int main(int argc, char *argv[]) { HANDLE hSerial; BOOL isSuc; char comPortName[MAX_STR_LEN]; HANDLE hWriteThread, hReadThread; isSuc = FALSE; sprintf_s(&comPortName[0], MAX_STR_LEN, "\\\\.\\COM14"); isLeaveThread = FALSE; if( FALSE == SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) ) { printf("ERROR : SetConsoleCtrlHandler , "); goto Flag_CloseSerial; }/*if */ hSerial = CreateFile(&comPortName[0], GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hSerial == INVALID_HANDLE_VALUE) { printf("ERROR : open %s , ", &comPortName[0]); goto Flag_CloseSerial; }/*if */ /*set timeout mode, the time limits of 2000 milli-sec*/ if(FALSE == SetSerialProperties(hSerial, CBR_57600, ONESTOPBIT, NOPARITY, 2000*MILLI_SEC)) goto Flag_CloseSerial; InitializeCriticalSection(&UARTThreadCriticalSection); hWriteThread = (HANDLE)_beginthreadex(NULL, 0, WriteThread, &hSerial, 0/*or CREATE_SUSPENDED*/, NULL); if (NULL == hWriteThread) { fprintf(stderr, "error : create confirming thread : "); goto Flag_CloseSerial; }/*if NULL == hConfirmThread*/ hReadThread = (HANDLE)_beginthreadex(NULL, 0, ReadThread, &hSerial, 0/*or CREATE_SUSPENDED*/, NULL); if (NULL == hReadThread) { fprintf(stderr, "error : create command thread : "); goto Flag_CloseSerial; }/*if NULL == hConfirmThread*/ if(FALSE == SetThreadPriority(hWriteThread, THREAD_PRIORITY_LOWEST)) { fprintf(stderr, "error :SetThreadPriority : "); goto Flag_CloseSerial; }/*if*/ WaitForSingleObject(hWriteThread, INFINITE); WaitForSingleObject(hReadThread, INFINITE); DeleteCriticalSection(&UARTThreadCriticalSection); isSuc = TRUE; Flag_CloseSerial: if(FALSE == isSuc) printf("%s\n", GetLastErrorMessage( GetLastError() )); isSuc = CloseHandle(hSerial); if(FALSE == isSuc) { printf("ERROR : close %s , %s\n", &comPortName[0], GetLastErrorMessage( GetLastError()) ); }/*if FALSE == isSuc*/ return 0; }/*main*/
In this example, I assume target com-port be COM14. You should modify it as your actual com-port number.
Note that the com-port string for CreateFile argument should be form of "\\.\COMX" (where the X is a natural number). If the one does the use simple formal "COMX" and X > 9 for CreateFile , the function would return NULL handle(INVALID_HANDLE_VALUE). More detail discussion could be refer to the thread on stackoverflow.
Most functions are intuitive ( if the person familiar with linux serial port programming ). The confusing functions is only one : SetCommTimeouts with structure COMMTIMEOUTS, for setting blocking or timeout mode. I have organize those in function SetSerialProperties,
沒有留言:
張貼留言