Serial Communication (WriteFile)
Hi all,
I require some guidance on Serial communication.
Currently, I have an array of data to send which are stored as
unsigned short int sendData[10];
so when I call writeFile, i pass the pointer to the start of sendData
WriteFile(hComm, sendData, dwToWrite, &dwWritten, &osWrite)
My question is What should I specify for the bytes to write field (dwToWrite)?
- Should I specify 20 OR
- Should I specify 2 and then keep incrementing the pointer to the next 2 bytes b4 i call WriteFile,
eg. WriteFile(hComm, sendData[1], 2, &dwWritten, &osWrite)
The reason why I am asking this question is I have another application file to READ in the data sent out (by the above codes), I cant seem to read in >1 byte of data... it only fills my receiver buffer with the 1st byte of data.
Thank you
FT
2 Attachment(s)
Re: Serial Communication (WriteFile)
I attached a couple of files I have used in the past for serial comms. They are pretty basic, but work.
Re: Serial Communication (WriteFile)
Some things to remember when writting or reading some data in this way.
1. Timeouts will be much longer than you intended. Check the parameters and the SetCommTimeouts() timeout structure.
2. You un-neccesarily make lots of calls down through the kernel, so you are wasting context-switches. Only a problem though if you want to write data continuously though; the scarry problem here is that because of UART buffering, your code may behave differently on machines with differing UART architectures because of the way the drivers handle them. Leads to bugs reported by users. (Asside, if you happen to be sending over anything other than RS232 and use a media-converter or anything in between, this approach can be fraught with problems too.)
3. Writting data in nibbles is good, especially if you use a state-machine, because it could leave your application free to process user input. Weigh this against either doing overlapped I/O or using a thread.
- the code in 'tmecham's post is a pretty good place to start learning. Good luck!
Re: Serial Communication (WriteFile)
Thank you to the both of your for the reply. I was looking at the codes for readFile as shown below and I got this question
int CSerial::ReadData( void *buffer, int limit )
{
if( !m_bOpened || m_hIDComDev == NULL ) return( 0 );
BOOL bReadStatus;
DWORD dwBytesRead, dwErrorFlags;
COMSTAT ComStat;
ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat );
if( !ComStat.cbInQue ) return( 0 );
dwBytesRead = (DWORD) ComStat.cbInQue;
if( limit < (int) dwBytesRead ) dwBytesRead = (DWORD) limit;
bReadStatus = ReadFile( m_hIDComDev, buffer, dwBytesRead, &dwBytesRead, &m_OverlappedRead );
if( !bReadStatus ){
if( GetLastError() == ERROR_IO_PENDING ){
WaitForSingleObject( m_OverlappedRead.hEvent, 2000 );
return( (int) dwBytesRead );
}
return( 0 );
}
return( (int) dwBytesRead );
}
The 2nd parameter of the readFile function takes in a pointer to the buffer that receives data from a file. Is the data type of this buffer crucial? Eg. can i specify unsigned short int for the buffer type yet my limit is specified as anything except for 2 bytes?
Re: Serial Communication (WriteFile)
I would not use anything other than a buffer-pointer to something 8-bit to recieve the eventual data: By specifying a 16-bit type, you are assuming that the data will have the intended endian-ness and also that it will be in sync/frame correctly. If not your application is just dealing with garbage. If the entire serial transfer is using short int only, and you have another way of getting back into sync, you are welcome to pass a buffer of short-int and it will work fine. But if the UART drops a single character, and both ends do not use a pause of at least 1 second when errors occur, they may never be able to get back into sync.
However! Here is a handy trick (code written on-the-fly)
Code:
#pragma pack(1)
typedef struct _telegram
{
unsigned char startB;
short value1;
short value2;
unsigned char crc;
} telegram;
#pragma pop
...
int ret = ReadFile(hFile, &telegram, sizeof(telegram), &dwBytesRead, 0);
...
if ((sizeof(telegram) == dwBytesRead) && CheckCRC(telegram))
{
mouseX = telegram.value1;
mouseY = telegram.value2;
}
I have used the pragma directive to byte-align structure members(remember to turn it off afterwards), and make packing/unpacking the data easier. Note that packed struct types like this can be re-used anywhere else in code elsewhere which does not need to know about the packing used (the compiller remembers it all for you).
Remember that when transfering data over any medium that does not do integrity-checks, for your, you want a CRC test or something right up-front before you try to access the data in any way.
Looking at your first post, you have 10 values, pack them into a struct, put a known-value in front of them like 0x5A5A, and then a known end-CRC made from the sum of all the preceding members of the struct...
Code:
typedef struct _telegram {
short int head;
short int data[10];
short int crc;
}
sum=telegram.head = 0x5A5A;
for (int i=0;i<10 ++i) sum+= telegram.data[i];
telegram.crc = sum;
...use the reverse of above to test the crc.
I assume you either use an acknowledging message to prompt the remote end, or have sizeable delays between data exchanges. You need to have one or the other, and you should be home free. Especially if you code the struct packing into a shared headder file, or document it like protocols do.
:)
Re: Serial Communication (WriteFile)
Thank you. Your explanation is very clear. Yes, I specified a 16bit type as its agreed btwn the sender and receiver that the data sent is word (16bits)aligned..
Thank you so much...
FT
Re: Serial Communication (WriteFile)
Good stuff, glad we could help.
Just to clarify for anyone else reading this sample, the data should be typed
unsigned short, to make it portable unless we ensure the correct compiler options.
try using the typedef to make future changes/platforms easier.
Code:
typedef WORD unsigned short;