My termios setup is modifying the first character read from the serial port using read(). I have a microcontroller talking to a linux box. The microcontroller responds to commands sent from the linux machine. The setup is as follows:
- microcontroller(PIC24F) RS485 port <--> RS485 to USB converter <--> Ubuntu PC.
When I run a terminal program such as Cutecom everything works as planned. I send a command character to the PIC and I get a response however when I use my command line program the first character is being modified. Here is my code:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#define DEVICE "/dev/ttyUSB0"
#define SPEED B38400
int main()
{
struct termios tio; //to hold serial port settings
struct termios stdio; //so we can accept user input
struct termios old_stdio; //save the current port settings
int tty_fd; //file descriptor for serial port
int res, n, res2, read1, wri;
char buf[255];
char buf2[255];
//save the current port settings
tcgetattr(STDOUT_FILENO,&old_stdio);
//setup serial port settings
bzero(&tio, sizeof(tio));
tio.c_iflag = 0;
tio.c_iflag = IGNPAR | IGNBRK | IXOFF;
tio.c_oflag = 0;
tio.c_cflag = CS8 | CREAD | CLOCAL; //8n1 see termios.h
tio.c_lflag = ICANON;
//open the serial port
tty_fd=open(DEVICE, O_RDWR | O_NOCTTY);
//set the serial port speed to SPEED
cfsetospeed(&tio,SPEED);
//apply to the serial port the settings made above
tcsetattr(tty_fd,TCSANOW,&tio);
for(n = 5; n > 0; n--)
{
printf("Please enter a command: ");
(void)fgets(buf2, 255, stdin);
(void)write(tty_fd, buf2, strlen(buf2));
printf("Ok. Waiting for reply.");
res = read(tty_fd, buf, 255);
printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2],buf[3],
buf[4]);
}
//close the serial port
close(tty_fd);
//restore the original port settings
tcsetattr(STDOUT_FILENO,TCSANOW,&old_stdio);
return EXIT_SUCCESS;
}
Here is an example of results I am getting.
- When the PIC sends "00000\n" the output is: Read: 6 START-16 48 48 48 48FINISH
- When the PIC sends "23456\n" the output is: Read: 6 START-14 51 52 53 54FINISH
- When the PIC sends "34567\n" the output is: Read: 6 START-14 52 53 54 55FINISH
- When the PIC sends "45678\n" the output is: Read: 6 START-12 53 54 55 56FINISH
- When the PIC sends "56789\n" the output is: Read: 6 START-12 54 55 56 57FINISH
For some reason the first character is getting messed up by some termios setting. It must be the termios settings as the same test inputs above are returned exactly when I run Cutecom. I have read the manual pages over and over, trying all different settings on the input control but no matter what I do cannot shakes this problem.
For an easy fix I can just shift my data across 1 character but want to avoid doing this.
Has anyone experienced such a problem or have any idea what to do about it?
Many thanks.
28/3/13 Great suggestion Austin. For those who are interested here are the two outputs:
First are the termios settings in my program
speed 38400 baud; rows 0; columns 0; line = 0; intr = ; quit = ; erase = ; kill = ; eof = ; eol = ; eol2 = ; swtch = ; start = ; stop = ; susp = ; rprnt = ; werase = ; lnext = ; flush = ; min = 0; time = 0; -parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
And the settings that cutecom uses
speed 38400 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 60; time = 1; -parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
I am still going through it all and will update the post when I make progress.
29/3/13 Still have the same problem. I even found the source code to Cutecom and followed the termios settings they use. Still the problem exists. That first character is corrupted!!!!
Here are the Termios settings from my program. Cannot set flush for some reason.
speed 38400 baud; rows 0; columns 0; line = 0; intr = ^?; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ; min = 60; time = 1; -parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
And my new code:
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <sys/ioctl.h> #define DEVICE "/dev/ttyUSB0" #define SPEED B38400 int main() { struct termios tio; //to hold serial port settings struct termios stdio; //so we can accept user input struct termios old_stdio; //save the current port settings int tty_fd; //file descriptor for serial port int retval, res, n, res2, read1, wri; char buf[255]; char buf2[255]; tty_fd = open(DEVICE, O_RDWR | O_NDELAY); if(tty_fd < 0) { perror(DEVICE); exit(-1); } printf("Init 1 complete.\n"); tcflush(tty_fd, TCIOFLUSH); int f = fcntl(tty_fd, F_GETFL, 0); fcntl(tty_fd, F_SETFL, f & ~O_NDELAY); retval = tcgetattr(tty_fd, &old_stdio); if(retval != 0) { perror(DEVICE); exit(-1); } printf("Init 2 complete.\n"); struct termios newtio; retval = tcgetattr(tty_fd, &newtio); if(retval != 0) { perror(DEVICE); exit(-1); } printf("Init 3 complete.\n"); cfsetospeed(&newtio, SPEED); cfsetispeed(&newtio, SPEED); newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8; newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~(PARENB | PARODD); newtio.c_cflag &= ~CRTSCTS; newtio.c_cflag &= ~CSTOPB; newtio.c_iflag = IGNBRK; newtio.c_iflag &= ~(IXON | IXOFF | IXANY); newtio.c_lflag = 0; newtio.c_oflag = 0; newtio.c_cc[VTIME] = 1; newtio.c_cc[VMIN] = 60; newtio.c_cc[VINTR] = 127; newtio.c_cc[VQUIT] = 28; newtio.c_cc[VERASE] = 8; newtio.c_cc[VKILL] = 21; newtio.c_cc[VEOF] = 4; newtio.c_cc[VSTOP] = 19; newtio.c_cc[VSTART] = 17; newtio.c_cc[VSUSP] = 26; newtio.c_cc[VREPRINT] = 18; newtio.c_cc[VFLSH] = 15; newtio.c_cc[VWERASE] = 23; newtio.c_cc[VLNEXT] = 22; retval = tcsetattr(tty_fd, TCSANOW, &newtio); if(retval != 0) { perror(DEVICE); exit(-1); } printf("Init 4 complete.\n"); int mcs = 0; ioctl(tty_fd, TIOCMGET, &mcs); mcs |= TIOCM_RTS; ioctl(tty_fd, TIOCMSET, &mcs); retval = tcgetattr(tty_fd, &newtio); if(retval != 0) { perror(DEVICE); exit(-1); } printf("Init 5 complete.\n"); newtio.c_cflag &= ~CRTSCTS; retval = tcsetattr(tty_fd, TCSANOW, &newtio); if(retval != 0) { perror(DEVICE); exit(-1); } printf("Init 6 complete.\n"); for(n = 5; n > 0; n--) { printf("Please enter a command: "); (void)fgets(buf2, 255, stdin); (void)write(tty_fd, buf2, strlen(buf2)); printf("Ok. Waiting for reply\n"); res = read(tty_fd, buf, 255); printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2], buf[3], buf[4]); } //restore the original port settings tcsetattr(tty_fd, TCSANOW, &old_stdio); close(tty_fd); return EXIT_SUCCESS; //return all good }
I am totally lost as to what can be done or where I should take it from here.
expected & 0xEE
... – Jonathan Leffler'0'
, 0x30, you're getting 0x10, and 0x30 & 0xDE == 0x10 (16). Similarly, when the first byte is'2'
(0x32), you're getting 14 or 0x0E; 0x3E & 0xDE = 0x0E. For'3'
(0x33), 0x33 & 0xDE == 0x0E (14); for'4'
(0x34), 0x34 & 0xDE == 0x0C (12); for'5'
(0x35), 0x35 & 0xDE == 0x0C (12). So, it is as if the first byte is being ANDed with 0xDE. I can't think of a good reason why it would be happening, but that does explain the pattern you see. – Jonathan Leffler#include <stdio.h> struct bits { unsigned char sent; unsigned char read; } values[] = { { '0', 16 }, { '2', 14 }, { '3', 14 }, { '4', 12 }, { '5', 12 }, }; enum { NUM_VALUES = sizeof(values) / sizeof(values[0]) }; unsigned char mask = 0xDE; int main(void) { for (int i = 0; i < NUM_VALUES; i++) { unsigned char r = values[i].read; unsigned char s = values[i].sent; printf("%d: sent '%c' = %2d = 0x%.2X; '%c' & 0x%2.X = 0x%.2X; read = %d = 0x%.2X\n", i, s, s, s, s, mask, s & mask, r, r); } return 0; }
– Jonathan Leffler