2

I need to send binary data over a serial port, without any bytes getting reinterpreted as control characters along the way. I'm currently setting up my serial port as follows:

#include <windows.h>

// open serial port
HANDLE hSerial;
hSerial = CreateFile ("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

// get serial parameters
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength = sizeof (dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
    cout << "error getting state\n";
    exit(0);
}

// set serial params
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity   = NOPARITY;
if (!SetCommState (hSerial, &dcbSerialParams)) {
    cout << "error setting parameters\n";
    exit(0);
}

// set time outs
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 10;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts (hSerial, &timeouts)) {
    cout << "problem setting timeout values\n";
    exit(0);
} else cout << "timeouts set\n";

When I issue ReadFile commands, I can get and display bytes from 0 to 255 with no problem. but I'm having no such luck with WriteFile. Is there a way to explicitly set a binary write mode?

EDIT

Ok, here's some more info. I have a windows machine and a linux single board computer hooked up through serial, the code above on the windows side is followed by:

unsigned char temp = 0;

bool keepReading = true;
while (keepReading) {
    DWORD dwBytesRead = 0;
    ReadFile (hSerial, &temp, 1, &dwBytesRead, NULL);
    if (1 == dwBytesRead) cout << (unsigned int) temp << " ";
    if (255 == temp) keepReading = false;
}
cout << endl;

bool keepWriting = true;
char send = 0;
while (keepWriting) {
    DWORD dwBytesWritten = 0;
    WriteFile (hSerial, &send, 1, &dwBytesWritten, NULL);
    send++;
    if (256 == send) keepWriting = false;
}

My code on the linux side looks like this:

int fd = open("/dev/ttymxc0", O_RDWR | O_NOCTTY);
struct termios options;
bzero (options, sizeof(options));
options.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
options.c_iflat = IGNPAR;
options.c_oflag = 0;
options.c_lflag = ICANON;
options.c_cc[VMIN] = 1;
options.c_CC[VTIME] = 0;
tcflush (fd, TCIFLUSH);
tcsetattr (fd, ICSANOW, &options);

bool keepWriting = true;
char send = 0;
while (keepWriting) {
    write (fd, &send, 1);
    send++;
    if (256 == send) keepWriting = false;
}

bool keepReading = true;
while (keepReading) {
    char temp = 0;
    int n = read (fd, &temp, 1);
    if (-1 == n) {
        perror ("Read error");
        keepReading = false;
    } else if (1 == n) {
        cout << temp << " ";
    }
    if (256 == temp) keepReading = false;

}
cout << endl;

close(fd);

I start up the code on both machines, and the first set of while loops runs fine. The terminal on the windows side displays 0 through 255. Then it just sits there. If I output the number of bytes read on the linux side for the second set of while loops, it continually gives me 0 bytes. This would indicate a closed port normally, but I just sent a bunch of info through it so how could that be?

3
  • Can you post what you have tried for WriteFile? There shouldn't be anything different as far as calling WriteFile versus ReadFile. Commented Oct 3, 2013 at 19:37
  • "Having no such luck" is not an appropriate problem description. There is no "binary write mode", it is always binary. You will probably have to look for the problem at the other end of the wire. Commented Oct 3, 2013 at 19:50
  • 1
    You should make sure DCB::fOutX and DCB::fInX are both set to 0. Commented Oct 3, 2013 at 20:00

3 Answers 3

4

As Jonathan Potter mentions, most likely you don't have XON/XOFF flow control turned off. Add these lines before the call to SetCommState:

dcbSerialParams.fOutX = 0;
dcbSerialParams.fInX  = 0;

Some other fields which you may need to set:

dcbSerialParams.fNull = 0;
dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
Sign up to request clarification or add additional context in comments.

1 Comment

I've given this a try, and the results are unchanged. One thing I've been thinking about is: It is possible to log in over the serial port, and in fact that is how you set the board up in the first place. Could the bytes send from Windows be captured by the terminal, rather than my program?
2

I think what may be happening is that Linux is detecting a break, and resetting the port, or the fact that canonical mode is set is messing it up. Try these settings in addition to what you have already:

    options.c_iflag |= IGNBRK;
    options.c_iflag &= ~BRKINT;
    options.c_iflag &= ~ICRNL;
    options.c_oflag = 0;
    options.c_lflag = 0;

1 Comment

This didn't seem to make a difference. I'm thinking along the same lines though, that some bytes are being interpreted as control characters. Though, it doesn't seem to matter which bytes I send.
0

Alright so I figured it out, rather, a co-worker did. On the linux side, in the file /etc/inittab I had to comment out the line:

T0:23:respawn:/sbin/getty -L ttymxc0 115200 vt100

This was grabbing the serial port in a way that made it unusable for receiving bytes. I now see the expected output.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.