Serial Programming: Difference between revisions
(→Tools) |
|||
(3 intermediate revisions by the same user not shown) | |||
Line 59: | Line 59: | ||
== Commands == |
== Commands == |
||
See [[Linux Commands#Serial]] for serial commands. |
See [[Linux Commands#Serial]] for serial commands. |
||
== Async Serial IO on POSIX == |
|||
Several motivations for async IO: |
|||
* Don't block thread on read operation |
|||
* Implement read timeout |
|||
* Allow canceling IO from another thread |
|||
Async IO is not an easy business :-(. Here we list some caveats / ideas. |
|||
=== pthreads and async signal handlers === |
|||
Apply extreme care when dealing with asyng signal handlers in multi-threaded environment, since pthreads functions are '''NOT''' async-signal safe (see [[POSIX]]). For instance, the following will likely dead-lock: |
|||
<source lang=c> |
|||
</source> |
|||
<source lang=c> |
|||
void signal_handler(int status) |
|||
{ |
|||
pthread_mutex_lock(& Ctx.Signal.mut); // BAD!!! Might dead-lock |
|||
Ctx.Signal.kicked = 1; |
|||
pthread_cond_signal(& Ctx.Signal.cond); // BAD!!! Might dead-lock |
|||
pthread_mutex_unlock(& Ctx.Signal.mut); // BAD!!! Might dead-lock |
|||
} |
|||
void setup(void) |
|||
{ |
|||
// ... |
|||
saio.sa_handler = signal_handler; |
|||
sigaction(SIGIO, &saio, NULL); |
|||
// ... |
|||
} |
|||
</source> |
|||
=== Read with timeout (not limited to serial) === |
|||
This can be done with <code>select</code>. Example below is copied from [http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/ Non-blocking user input in loop without ncurses] and from [http://stackoverflow.com/questions/3711830/set-a-timeout-for-reading-stdin Set a timeout for reading stdin]: |
|||
{| class=wikitable |
|||
| valign=top | |
|||
<source lang=c> |
|||
// if != 0, then there is data to be read on stdin |
|||
int kbhit() |
|||
{ |
|||
// timeout structure passed into select |
|||
struct timeval tv; |
|||
// fd_set passed into select |
|||
fd_set fds; |
|||
// Set up the timeout. here we can wait for 1 second |
|||
tv.tv_sec = 1; |
|||
tv.tv_usec = 0; |
|||
// Zero out the fd_set - make sure it's pristine |
|||
FD_ZERO(&fds); |
|||
// Set the FD that we want to read |
|||
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 |
|||
// select takes the last file descriptor value + 1 |
|||
// in the fdset to check, the fdset for reads, |
|||
// writes, and errors. We are only passing in reads. |
|||
// the last parameter is the timeout. |
|||
// select will return if an FD is ready or the |
|||
// timeout has occurred |
|||
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); |
|||
// return 0 if STDIN is not ready to be read. |
|||
return FD_ISSET(STDIN_FILENO, &fds); |
|||
} |
|||
</source> |
|||
| |
|||
Turn on/off canonical mode (otherwise kbhit will only react to ''ENTER'' key): |
|||
<source lang=c> |
|||
void nonblock(int state) |
|||
{ |
|||
struct termios ttystate; |
|||
//get the terminal state |
|||
tcgetattr(STDIN_FILENO, &ttystate); |
|||
if (state==NB_ENABLE) |
|||
{ |
|||
//turn off canonical mode |
|||
ttystate.c_lflag &= ~ICANON; |
|||
//minimum of number input read. |
|||
ttystate.c_cc[VMIN] = 1; |
|||
} |
|||
else if (state==NB_DISABLE) |
|||
{ |
|||
//turn on canonical mode |
|||
ttystate.c_lflag |= ICANON; |
|||
} |
|||
//set the terminal attributes. |
|||
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); |
|||
} |
|||
</source> |
|||
| |
|||
<source lang=c> |
|||
int main() |
|||
{ |
|||
char c; |
|||
int i=0; |
|||
nonblock(NB_ENABLE); |
|||
while(!i) |
|||
{ |
|||
usleep(1); |
|||
i=kbhit(); |
|||
if (i!=0) |
|||
{ |
|||
c=fgetc(stdin); |
|||
if (c=='q') |
|||
i=1; |
|||
else |
|||
i=0; |
|||
} |
|||
fprintf(stderr,"%d ",i); |
|||
} |
|||
printf("\n you hit %c. \n",c); |
|||
nonblock(NB_DISABLE); |
|||
return 0; |
|||
} |
|||
</source> |
|||
|} |
|||
The same example with '''[http://en.wikipedia.org/wiki/Curses_(programming_library) curses]''' (from same links): |
|||
<source lang=c> |
|||
#include<stdio.h> |
|||
#include<curses.h> |
|||
#include<unistd.h> |
|||
int main () |
|||
{ |
|||
int i=0; |
|||
initscr(); //in ncurses |
|||
timeout(0); |
|||
while(!i) |
|||
{ |
|||
usleep(1); |
|||
i=getch(); |
|||
printw("%d ",i); |
|||
if(i>0) |
|||
i=1; |
|||
else |
|||
i=0; |
|||
} |
|||
endwin(); |
|||
printf("\nhitkb end\n"); |
|||
return 0; |
|||
} |
|||
</source> |
|||
=== Read with Timeout for Serial IO === |
|||
See [http://www.easysw.com/~mike/serial/serial.html Serial Programming Guide for POSIX Operating Systems]. |
|||
== Troubleshooting == |
== Troubleshooting == |
Latest revision as of 00:45, 4 November 2012
References
- Serial Programming HOWTO
- Serial Programming Guide for POSIX Operating System
- Serial Port Programming in Windows and Linux
How-To
Flush Serial Buffers
Use constant TCSAFLUSH
for function tcsetattr
(see [1] and [2]):
Constant | Description |
---|---|
TCSANOW | Make changes now without waiting for data to complete |
TCSADRAIN | Wait until everything has been transmitted |
TCSAFLUSH | Flush input and output buffers and make the change |
This can be done even on a serial port that is already configured:
struct termios options;
tcgetattr(fd, &options); // Get current configuration of this port
cfsetispeed(&options, B115200); // Configure some port options (e.g. baud rate...)
...
tcsetattr(fd, TCSANOW, &options); // Apply the new settings immediately
... // Read / Write to the port...
tcsetattr(fd, TCSAFLUSH, &options); // Flush IO buffer
Tools
Minicom
Minicom (wikipedia) is a text-based modem control and terminal emulation program for Linux.
It can be used to debug / test serial connections, but unfortunately it is only ascii based and does not allow to send / receive easily data in hexadecimal format (except via sending / receiving files).
Minicom is in the repositories:
sudo apt-get install minicom
Cutecom
Cutecom is a handy and easy graphic tool to test/debug embedded devices controlled via serial link. This tool allows sending / receiving data in hexadecimal format (select Hex input and Hex output).
Cutecom is in the repositories:
sudo apt-get install cutecom
interceptty
interceptty is a program that sits between a serial port (or other tty-style device) and an application, and logs everything that passes through it.
SerLook
SerLook is a KDE application for inspecting data going over serial lines.
Commands
See Linux Commands#Serial for serial commands.
Async Serial IO on POSIX
Several motivations for async IO:
- Don't block thread on read operation
- Implement read timeout
- Allow canceling IO from another thread
Async IO is not an easy business :-(. Here we list some caveats / ideas.
pthreads and async signal handlers
Apply extreme care when dealing with asyng signal handlers in multi-threaded environment, since pthreads functions are NOT async-signal safe (see POSIX). For instance, the following will likely dead-lock:
void signal_handler(int status)
{
pthread_mutex_lock(& Ctx.Signal.mut); // BAD!!! Might dead-lock
Ctx.Signal.kicked = 1;
pthread_cond_signal(& Ctx.Signal.cond); // BAD!!! Might dead-lock
pthread_mutex_unlock(& Ctx.Signal.mut); // BAD!!! Might dead-lock
}
void setup(void)
{
// ...
saio.sa_handler = signal_handler;
sigaction(SIGIO, &saio, NULL);
// ...
}
Read with timeout (not limited to serial)
This can be done with select
. Example below is copied from Non-blocking user input in loop without ncurses and from Set a timeout for reading stdin:
// if != 0, then there is data to be read on stdin
int kbhit()
{
// timeout structure passed into select
struct timeval tv;
// fd_set passed into select
fd_set fds;
// Set up the timeout. here we can wait for 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
// Zero out the fd_set - make sure it's pristine
FD_ZERO(&fds);
// Set the FD that we want to read
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
// select takes the last file descriptor value + 1
// in the fdset to check, the fdset for reads,
// writes, and errors. We are only passing in reads.
// the last parameter is the timeout.
// select will return if an FD is ready or the
// timeout has occurred
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
// return 0 if STDIN is not ready to be read.
return FD_ISSET(STDIN_FILENO, &fds);
}
|
Turn on/off canonical mode (otherwise kbhit will only react to ENTER key): void nonblock(int state)
{
struct termios ttystate;
//get the terminal state
tcgetattr(STDIN_FILENO, &ttystate);
if (state==NB_ENABLE)
{
//turn off canonical mode
ttystate.c_lflag &= ~ICANON;
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
}
else if (state==NB_DISABLE)
{
//turn on canonical mode
ttystate.c_lflag |= ICANON;
}
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
|
int main()
{
char c;
int i=0;
nonblock(NB_ENABLE);
while(!i)
{
usleep(1);
i=kbhit();
if (i!=0)
{
c=fgetc(stdin);
if (c=='q')
i=1;
else
i=0;
}
fprintf(stderr,"%d ",i);
}
printf("\n you hit %c. \n",c);
nonblock(NB_DISABLE);
return 0;
}
|
The same example with curses (from same links):
#include<stdio.h>
#include<curses.h>
#include<unistd.h>
int main ()
{
int i=0;
initscr(); //in ncurses
timeout(0);
while(!i)
{
usleep(1);
i=getch();
printw("%d ",i);
if(i>0)
i=1;
else
i=0;
}
endwin();
printf("\nhitkb end\n");
return 0;
}
Read with Timeout for Serial IO
See Serial Programming Guide for POSIX Operating Systems.
Troubleshooting
Shunt RXD/TXD pin
An easy way to test whether the serial line is working properly is simply to shunt the RXD and TXD line of the serial connector (2nd and 3rd pin on a typical DE9 RS232 connector). This way anything sent to the serial port is echoed back.
- Open a terminal connected to the serial port,
- Shunt RXD and TXD (2nd and 3rd pin),
- Type character and verify that the same character are echoed back.
_______________________
/ 1 5 \
\ o o o o o /
\ 6 9 /
\ o o o o /
\_________________/
|
On a D-sub DE9 RS232 connector, the top left pin is the 1st pin, the bottom right is the 9th pin. The pinout is as follows:
|