LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-27-2012, 02:56 AM   #1
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Rep: Reputation: Disabled
Writing ok on a serial port, reading garbage


Greetings. First post here, so hope I do not break any rule.

I am an embedded developer who has started to move from the Windows world to Linux. Right now I am very happy with the change, since I can do great things with just a few lines of code, be it scripts, plain c or whatever. Everything can be automated, you can do almost anything, and the community seems much more willing to help and share experiences.

But there is always a "but". Right now, the only thing I am having trouble is talking to embedded devices from a PC with linux, having Linux on it. I have a small piece of code that sends ok the values I want, but if I have stopped the embedded device (while debugging, as an example) the PC reads garbage from the port. Well, not exactly garbage: seems to be reading the last bytes I sent. That would probably be related to something I do wrong in my code, but can't find it.

Oh, before posting any code, please forgive me for any mistakes in the text. I am not english speaker, so probably you are very ashamed of me if you have read until here. Don't be very rude I am still trying to improve.

Well, let's see the code, is just a small piece (I like to verify each small part in a different program, and later just make all of them fit together).

Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>

int main (void) {
	FILE *PuertoSerie;
	int n_escr,n_leido;
	unsigned char BufEnvio[11];
	unsigned char BufRecep[11];
	char nom_puerto[20] = "/dev/ttyO2";
	int ChkSum;

	printf("Starting\n");

	SerialPort = fopen("/dev/ttyO2","rb+");

	if (SerialPort == NULL)
	{
		printf("ERROR -1: Error opening port %s\n",nom_puerto);
		return -1;
	}
	printf("Port open (%s)\n",nom_puerto);

	// Data that will be sent over the port
	BufEnvio[0]=0x0F;
	BufEnvio[1]=0x55;
	BufEnvio[2]=0x00;
	BufEnvio[3]=0x55;
	BufEnvio[4]=0xF0;

	n_escr = fwrite(BufEnvio, 1, 5, SerialPort);
	if (n_escr<0)
	{
		printf("ERROR -2: Error writing on port %s\n",nom_puerto);
		fclose(SerialPort);
		return -2;
	}

	n_leido = 0;
	do{
		n_leido = fread(BufRecep, 1, 11, SerialPort);
		if (n_leido<0)
		{
			printf("ERROR -3: Error reading on port %s\n",nom_puerto);
			fclose(SerialPort);
			return -3;
		}
	}while(n_leido<1);

	if((BufRecep[0]==0x0F) && (BufRecep[1]==0x55) && (BufRecep[9]==0x55) && (BufRecep[10]==0xF0))
	{
		ChkSum = BufRecep[2]+BufRecep[3]+BufRecep[4]+BufRecep[5]+BufRecep[6]+BufRecep[7];
		ChkSum = ChkSum % 255;
		if (ChkSum == BufRecep[8])
		{
			// All went fine
                        printf("Everything went fine. Program stops\n");
                        return 0;
		}
		else
		{
			printf("Error -5: ChkSum is wrong --> I have %d and received %d\n",ChkSum,BufRecep[8]);
			return -5;
		}
	}
	else
	{
		printf("ERROR -4: Frame error\n");
		printf("Element  \t value \t\thexadecimal\n");
		for(n_escr=0;n_escr<n_leido;n_escr++)
		{
			printf("   #%d\t\t  %03d\t\t    0x%02x\n", n_escr, BufRecep[n_escr], BufRecep[n_escr]);
		}
		return -4;
	}
}
(Sorry if I have any variable with wrong name: I have translated comments, messages and variables, since it all was in spanish in my program)

Thanks in advance!

EDIT: I noticed that, since fread is a blocking call, this part:
Code:
do{
		n_leido = fread(BufRecep, 1, 11, SerialPort);
		if (n_leido<0)
		{
			printf("ERROR -3: Error reading on port %s\n",nom_puerto);
			fclose(SerialPort);
			return -3;
		}
	}while(n_leido<1);
Can be changed into:
Code:
	n_leido = fread(BufRecep, 1, 11, SerialPort);
		if (n_leido<0)
		{
			printf("ERROR -3: Error reading on port %s\n",nom_puerto);
			fclose(SerialPort);
			return -3;
		}
(Just deleting the "while", since it will be blocked until something is read... Anyway, behavior remains the same.

Last edited by JeToMad; 03-27-2012 at 03:41 AM. Reason: Adding some info...
 
Old 03-27-2012, 05:22 AM   #2
firstfire
Member
 
Registered: Mar 2006
Location: Ekaterinburg, Russia
Distribution: Debian, Ubuntu
Posts: 709

Rep: Reputation: 428Reputation: 428Reputation: 428Reputation: 428Reputation: 428
Hi.

Welcome to LQ!

Here is a good tutorial about linux serial port programming.

You probably should set up serial port properly, that is set baud rate, parity, stop bits etc.

EDIT: here is another tutorial.

Last edited by firstfire; 03-27-2012 at 05:26 AM.
 
Old 03-27-2012, 05:28 AM   #3
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
Yup, I have looked at some tutorials, and this is one of them.

Anyways, the settings of the port does not seem to be the problem, since the embedded system receive correctly the data sent by the computer. In fact, I first looked at the settings of the PC and later configured my uC, since I am more comfortable messing with the uC code than with the PC. As said, receiving is OK (Receiving from the embedded side, sending by the PC) and then, embedded device seems to send data back correctly, but PC shows that nothing is received, or just read garbage/bad data.

I will look again at the hardware (maybe something has gone wrong during the test and assembly of the board that contains the uC) with an oscilloscope, but seemed ok first time.

EDIT: I chosed using "fopen" since seemed ok for sending arrays of plain bytes. The examples I have seen using "open", just send characters, not plain binary data. Is that correct, or am I misunderstanding it completely? As I said, I am pretty new to Linux, and in Windows I could chose to open the port for binary or character data, but here don't seem to have another way that to open with "fopen" and use "fread" and "fwrite"...

EDIT AGAIN: Hardware is OK. Tried this with a Windows machine and does work. Here is the code in c#:
Code:
//Receiving:
private void SPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
            while (SPort.BytesToRead > 0)
            {
                int Data = SPort.ReadByte();

                ShowData(Data);
            }
            return;
}

//Sending

private void btnSend_Click(object sender, EventArgs e)
        {
            byte[] sendBuffer = new byte[5];
            sendBuffer[0] = 0x0F;
            sendBuffer[1] = 0x55;
            sendBuffer[2] = 0x0;
            sendBuffer[3] = 0x55;
            sendBuffer[4] = 0xF0;
            SPort.Write(sendBuffer, 0, 5);
        }

Last edited by JeToMad; 03-27-2012 at 05:53 AM. Reason: Adding code
 
Old 03-27-2012, 06:47 AM   #4
firstfire
Member
 
Registered: Mar 2006
Location: Ekaterinburg, Russia
Distribution: Debian, Ubuntu
Posts: 709

Rep: Reputation: 428Reputation: 428Reputation: 428Reputation: 428Reputation: 428
Hi.

AFAIK fwrite()/fread() by default use buffered IO, while write()/read() do not. You can send "arrays of plain bytes" using both fwrite() and write(), but the latter will write data as soon as possible. You can control buffering type using the setvbuf() function.

To see default port settings try
Code:
stty -a -F /dev/ttyO2
See man stty for details.
 
1 members found this post helpful.
Old 03-27-2012, 06:56 AM   #5
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by firstfire View Post
Hi.

AFAIK fwrite()/fread() by default use buffered IO, while write()/read() do not. You can send "arrays of plain bytes" using both fwrite() and write(), but the latter will write data as soon as possible. You can control buffering type using the setvbuf() function.
But I understand that fread() (and fwrite()) may take long to read/write (because of the buffering on the first ones), but should read/write ok anyways, didn't it?

Anyway, I will try to change to read()/write() and see if behavior changes...

Seems that I have just not readed enough (I mean myself, have not read enough documentation). Luckily, in the Linux world, it seems to me that you have as many documentation as you want

Thank you very much!
 
Old 03-27-2012, 07:02 AM   #6
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,871
Blog Entries: 1

Rep: Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871Reputation: 1871
1. Perhaps you should use low-level access: read, write, select/poll
2. And termios.tcgetattr/tcsetattrs to get and set options
3. Are your sure you device is /dev/tty02? If it is a real serial port, it should be /dev/ttyS0:

$ ls -l /dev/ttyS0
crw-rw---- 1 root dialout 4, 64 Mar 27 12:07 /dev/ttyS0
 
Old 03-27-2012, 07:58 AM   #7
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
Yep, the Linux PC is really a "Pandaboard", which is based on OMAP4 (ARM based, not x86), and it's serial ports are ttyOx instead of ttySx.

I am just reading info in order to try read()/write() instead of fread()/fwrite(), as suggested in earlier post.

Thank you very much!

EDIT: By the way, while looking for info I have seen some mentions to the need of a "0x0D" to "end" the "message". So I added that at the end of the data sent by the embedded device, and now the Linux program reads the data... So, your were right, I need to go for deeper functions (hope read()/write() are enough low-level for this...) Maybe this means that opening a ttySx with "fopen()" is like having some kind of hyperterminal? Anyways, I think that this is somehow solved, thanks to all of you!

YET ANOTHER EDIT: I was too quick to mark this as solved... Using read() needs too the 0x0D...

I have followed this:
http://tldp.org/HOWTO/Serial-Program...15.html#AEN144

But do not find anything that allows me to "force read" without waiting for 0x0D to arrive... żAny help?

Last edited by JeToMad; 03-27-2012 at 09:26 AM. Reason: More info
 
Old 03-27-2012, 09:34 AM   #8
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
The '0x0d' is a carriage return character. To me, this indicates that your port is configured in 'cooked' (canonical) mode, meaning it is reading and writing data in line-at-a-time fashion. You probably want to use 'raw' (non-canonical) mode.
--- rod.
 
Old 03-27-2012, 09:58 AM   #9
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
Well, messing with the termio flags has made the sent bytes to stop being sent. I should probably have not touched this. In fact, I am thinking in going back and just adding that 0x0D at the end, since for this program it will not meant any harm. But I usually prefer to have a "fully working method". Can the tty be set to "raw mode" without messing with the rest of the parameters?
 
Old 03-27-2012, 10:53 AM   #10
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Did you clear the OPOST bit in c_oflag? That should cause the output to operate in raw mode. Raw vs. cooked mode should be fully independent from other configurations, although it is conventional for some bits to be used in conjunction with certain others.
--- rod.

Last edited by theNbomr; 03-27-2012 at 10:55 AM.
 
Old 03-27-2012, 11:05 AM   #11
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
Well, in fact, I do not clear anything, instead of doing so, I start from "zero" and set the bits needed:

Code:
 tcgetattr(fd,&oldtio); /* save current port settings */
        
        bzero(&newtio, sizeof(newtio));
        newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
        newtio.c_iflag = IGNPAR;
        newtio.c_oflag = 0;
        
        /* set input mode (non-canonical, no echo,...) */
        newtio.c_lflag = 0;
         
        newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
        newtio.c_cc[VMIN]     = 11;   /* blocking read until 5 chars received */
        
        tcflush(fd, TCIFLUSH);
        tcsetattr(fd,TCSANOW,&newtio);
(As seen in "Non-Canonical Input Processing" here: http://tldp.org/HOWTO/Serial-Program...15.html#AEN129)

This seems to work, since if I combine this config with the fread()/fwrite() methods, then I have correct behavior. Probably all of this is because of my lack of knowledge on how Linux works, but it is really annoying...
 
Old 03-27-2012, 11:51 AM   #12
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Very often, the price of flexibility is complexity. This seems to be one of those cases. If there was only ever just one standard behavior for serial ports, we could do away with a good deal of the complexity.
This is one case where the terminal-based heritage of Unix/Linux shows itself. Most of the modern uses of serial ports are for talking to external devices, whereas the serial port support was developed at a time when most users connected to the host via serial terminals. A driver sufficiently flexible to handle both cases carries an accordant weight in complexity.
The Windows world with which you are more familiar, is relatively young, and doesn't have to deal with serial terminals.

--- rod.

Last edited by theNbomr; 03-28-2012 at 09:54 AM.
 
Old 03-28-2012, 02:11 AM   #13
JeToMad
LQ Newbie
 
Registered: Mar 2012
Location: Valencia, Spain
Distribution: Fedora 15, Ubuntu 11.10
Posts: 15

Original Poster
Rep: Reputation: Disabled
It's not just coming from a Windows world. Before developing for Windows, I have been developing for just small embedded devices, in which you had not to worry with an OS at all. You just set the function registers, that were clearly detailed at the datasheet, and started to work. Well, until just a couple of years I did it in assembler! But then, starting development for windows machines was not so hard. Most things made no sense, but they worked if you google a bit. With Linux, you may google it, and it will need still some work, wich is fine, since that is the way to really know what you are doing, but it's hard to tell the boss. I mean, I started using Linux at home too, and when I have a problem, I just want to understand what happens, and how can I solve it. But, at work... Boss want's only the system to work properly, as soon as possible, and does not care about if you have understood the problem at all. So, when later someone else looks at the code... It's not a problem of documentation, How could you document something you don't understand correctly?

Well, just "venting" a little. It just works, so for the boss it's ok.

Thank you all!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Writing/reading to a modem via serial device? R00ts Programming 3 01-06-2011 05:02 PM
Reading/Writing to serial ports (Rs-232). linux_chris Linux - Software 9 12-10-2008 01:05 PM
Reading and Writing to a Serial Port without SU Steef223 Programming 2 06-16-2008 09:16 AM
Facing problems in reading from and writing to serial port. vineel Linux - Hardware 2 02-27-2008 02:18 PM
reading and writing to a serial device (modem) Xanadu Linux - Laptop and Netbook 0 01-04-2005 10:05 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:39 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration