串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,具有连接线少,通讯简单,得到广泛的使用。
Linux对所有设备的访问是通过设备文件来进行的,串口也是这样,为了访问串口,只需打开其设备文件即可操作串口设备。在linux系统下面,每一个串口设备都有设备文件与其关联,设备文件位于系统的/dev目录下面。如linux下的/ttyS0,/ttyS1分别表示的是串口1和串口2。
在串口编程中,比较重要的是串口的设置,我们要设置的部分包括:波特率,数据位,停止位,奇偶校验位;要注意的是,每台机器的串口默认设置可能是不同的,如果你没设置这些,仅仅按照默认设置进行发送数据,很可能出现n多异想不到而又查不出来的情况。
1) 设置波特率
|
2) 设置属性:奇偶校验位、数据位、停止位。主要设置<termbits.h>中的termios结构体即可:
|
有相应的函数供获取和设置属性:
|
3) 打开、关闭和读写串口。串口作为设备文件,可以直接用文件描述符来进行操作。
|
网上的一个例子:
/*串口设备无论是在工控领域,还是在 嵌入式设备领域,应用都非常广泛。而串口编程也就显得必不可少。
偶然的一次机会,需要使用串口,而且操作系统还要求是Linux,因此,趁着这次机会,综合别人的代码,
进行了一次整理和封装。具体的封装格式为C代码,这样做是为了很好的移植性,使它可以在C和C++环境下,
都可以编译和使用。代码的头文件如下: */
///////////////////////////////////////////////////////////////////////////////
//filename:stty.h
# ifndef __STTY_H__
# define __STTY_H__
//包含头文件
# include < stdio. h>
# include < stdlib. h>
# include < unistd. h>
# include < sys/ types. h>
# include < sys/ stat. h>
# include < fcntl. h>
# include < termios. h>
# include < errno . h>
# include < pthread. h>
//
// 串口设备信息结构
typedef struct tty_info_t
{
int fd; // 串口设备ID
pthread_mutex_t mt; // 线程同步互斥对象
char name[ 24] ; // 串口设备名称,例:"/dev/ttyS0"
struct termios ntm; // 新的串口设备选项
struct termios otm; // 旧的串口设备选项
} TTY_INFO;
//
// 串口操作函数
TTY_INFO * readyTTY( int id) ;
int setTTYSpeed( TTY_INFO * ptty, int speed) ;
int setTTYParity( TTY_INFO * ptty, int databits, int parity, int stopbits) ;
int cleanTTY( TTY_INFO * ptty) ;
int sendnTTY( TTY_INFO * ptty, char * pbuf, int size) ;
int recvnTTY( TTY_INFO * ptty, char * pbuf, int size) ;
int lockTTY( TTY_INFO * ptty) ;
int unlockTTY( TTY_INFO * ptty) ;
# endif
/*从头文件中的函数定义不难看出,函数的功能,使用过程如下:
(1) 打开串口设备,调用函数setTTYSpeed();
(2) 设置串口读写的波特率,调用函数setTTYSpeed();
(3) 设置串口的属性,包括停止位、校验位、数据位等,调用函数setTTYParity();
(4) 向串口写入数据,调用函数sendnTTY();
(5) 从串口读出数据,调用函数recvnTTY();
(6) 操作完成后,需要调用函数cleanTTY()来释放申请的串口信息接口;
其中,lockTTY()和unlockTTY()是为了能够在多线程中使用。在读写操作的前后,需要锁定和释放串口资源。
具体的使用方法,在代码实现的原文件中,main()函数中进行了演示。下面就是源代码文件: */
////////////////////////////////////////////////////////////////////////////////
//stty.c
# include < stdio. h>
# include < sys/ ioctl. h>
# include "stty.h"
///////////////////////////////////////////////////////////////////////////////
// 初始化串口设备并进行原有设置的保存
TTY_INFO * readyTTY( int id)
{
TTY_INFO * ptty;
ptty = ( TTY_INFO * ) malloc ( sizeof ( TTY_INFO) ) ;
if ( ptty = = NULL )
return NULL ;
memset ( ptty, 0, sizeof ( TTY_INFO) ) ;
pthread_mutex_init ( & ptty- > mt, NULL ) ;
sprintf ( ptty- > name, "/dev/ttyS%d" , id) ;
//
// 打开并且设置串口
ptty- > fd = open ( ptty- > name, O_RDWR | O_NOCTTY | O_NDELAY) ;
if ( ptty- > fd < 0)
{
free ( ptty) ;
return NULL ;
}
//
// 取得并且保存原来的设置
tcgetattr( ptty- > fd, & ptty- > otm) ;
return ptty;
}
///////////////////////////////////////////////////////////////////////////////
// 清理串口设备资源
int cleanTTY( TTY_INFO * ptty)
{
//
// 关闭打开的串口设备
if ( ptty- > fd> 0)
{
tcsetattr( ptty- > fd, TCSANOW, & ptty- > otm) ;
close ( ptty- > fd) ;
ptty- > fd = - 1;
free ( ptty) ;
ptty = NULL ;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// 设置串口通信速率
// ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针
// speed 参数类型(int),用来设置串口的波特率
// return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值
///////////////////////////////////////////////////////////////////////////////
int setTTYSpeed( TTY_INFO * ptty, int speed)
{
int i;
//
// 进行新的串口设置,数据位为8位
bzero( & ptty- > ntm, sizeof ( ptty- > ntm) ) ;
tcgetattr( ptty- > fd, & ptty- > ntm) ;
ptty- > ntm. c_cflag = /*CS8 |*/ CLOCAL | CREAD;
switch ( speed)
{
case 300:
ptty- > ntm. c_cflag | = B300;
break ;
case 1200:
ptty- > ntm. c_cflag | = B1200;
break ;
case 2400:
ptty- > ntm. c_cflag | = B2400;
break ;
case 4800:
ptty- > ntm. c_cflag | = B4800;
break ;
case 9600:
ptty- > ntm. c_cflag | = B9600;
break ;
case 19200:
ptty- > ntm. c_cflag | = B19200;
break ;
case 38400:
ptty- > ntm. c_cflag | = B38400;
break ;
case 115200:
ptty- > ntm. c_cflag | = B115200;
break ;
}
ptty- > ntm. c_iflag = IGNPAR;
ptty- > ntm. c_oflag = 0;
//
//
tcflush( ptty- > fd, TCIFLUSH) ;
tcsetattr( ptty- > fd, TCSANOW, & ptty- > ntm) ;
//
//
return 0;
}
// 设置串口数据位,停止位和效验位
// ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针
// databits 参数类型(int), 数据位,取值为7或者8
// stopbits 参数类型(int),停止位,取值为1或者2
// parity 参数类型(int),效验类型 取值为N,E,O,,S
// return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值
///////////////////////////////////////////////////////////////////////////////
int setTTYParity( TTY_INFO * ptty, int databits, int parity, int stopbits)
{
//
// 取得串口设置
if ( tcgetattr( ptty- > fd, & ptty- > ntm) ! = 0)
{
printf ( "SetupSerial [%s]/n" , ptty- > name) ;
return 1;
}
bzero( & ptty- > ntm, sizeof ( ptty- > ntm) ) ;
ptty- > ntm. c_cflag = CS8 | CLOCAL | CREAD;
ptty- > ntm. c_iflag = IGNPAR;
ptty- > ntm. c_oflag = 0;
//
// 设置串口的各种参数
ptty- > ntm. c_cflag & = ~ CSIZE;
switch ( databits)
{ //设置数据位数
case 7:
ptty- > ntm. c_cflag | = CS7;
break ;
case 8:
ptty- > ntm. c_cflag | = CS8;
break ;
default :
printf ( "Unsupported data size/n" ) ;
return 5;
}
//
//
switch ( parity)
{ // 设置奇偶校验位数
case n:
case N:
ptty- > ntm. c_cflag & = ~ PARENB; /* Clear parity enable */
ptty- > ntm. c_iflag & = ~ INPCK; /* Enable parity checking */
break ;
case o:
case O:
ptty- > ntm. c_cflag | = ( PARODD| PARENB) ; /* 设置为奇效验*/
ptty- > ntm. c_iflag | = INPCK; /* Disnable parity checking */
break ;
case e:
case E:
ptty- > ntm. c_cflag | = PARENB; /* Enable parity */
ptty- > ntm. c_cflag & = ~ PARODD; /* 转换为偶效验*/
ptty- > ntm. c_iflag | = INPCK; /* Disnable parity checking */
break ;
case S:
case s: /*as no parity*/
ptty- > ntm. c_cflag & = ~ PARENB;
ptty- > ntm. c_cflag & = ~ CSTOPB;
break ;
default :
printf ( "Unsupported parity/n" ) ;
return 2;
}
//
// 设置停止位
switch ( stopbits)
{
case 1:
ptty- > ntm. c_cflag & = ~ CSTOPB;
break ;
case 2:
ptty- > ntm. c_cflag | = CSTOPB;
break ;
default :
printf ( "Unsupported stop bits/n" ) ;
return 3;
}
//
//
ptty- > ntm. c_lflag = 0;
ptty- > ntm. c_cc[ VTIME] = 0; // inter-character timer unused
ptty- > ntm. c_cc[ VMIN] = 1; // blocking read until 1 chars received
tcflush( ptty- > fd, TCIFLUSH) ;
if ( tcsetattr( ptty- > fd, TCSANOW, & ptty- > ntm) ! = 0)
{
printf ( "SetupSerial /n" ) ;
return 4;
}
return 0;
}
int recvnTTY( TTY_INFO * ptty, char * pbuf, int size)
{
int ret, left , bytes;
left = size;
while ( left > 0)
{
ret = 0;
bytes = 0;
pthread_mutex_lock ( & ptty- > mt) ;
ioctl( ptty- > fd, FIONREAD, & bytes) ;
if ( bytes> 0)
{
ret = read ( ptty- > fd, pbuf, left ) ;
}
pthread_mutex_unlock ( & ptty- > mt) ;
if ( ret > 0)
{
left - = ret;
pbuf + = ret;
}
usleep ( 100) ;
}
return size - left ;
}
int sendnTTY( TTY_INFO * ptty, char * pbuf, int size)
{
int ret, nleft;
char * ptmp;
ret = 0;
nleft = size;
ptmp = pbuf;
while ( nleft> 0)
{
pthread_mutex_lock ( & ptty- > mt) ;
ret = write ( ptty- > fd, ptmp, nleft) ;
pthread_mutex_unlock ( & ptty- > mt) ;
if ( ret > 0)
{
nleft - = ret;
ptmp + = ret;
}
//usleep(100);
}
return size - nleft;
}
int lockTTY( TTY_INFO * ptty)
{
if ( ptty- > fd < 0)
{
return 1;
}
return flock( ptty- > fd, LOCK_EX) ;
}
int unlockTTY( TTY_INFO * ptty)
{
if ( ptty- > fd < 0)
{
return 1;
}
return flock( ptty- > fd, LOCK_UN) ;
}
# ifdef LEAF_TTY_TEST
///////////////////////////////////////////////////////////////////////////////
// 接口测试
int main( int argc, char * * argv)
{
TTY_INFO * ptty;
int nbyte, idx;
unsigned char cc[ 16] ;
ptty = readyTTY( 0) ;
if ( ptty = = NULL )
{
printf ( "readyTTY(0) error/n" ) ;
return 1;
}
//
//
lockTTY( ptty) ;
if ( setTTYSpeed( ptty, 9600) > 0)
{
printf ( "setTTYSpeed() error/n" ) ;
return - 1;
}
if ( setTTYParity( ptty, 8, N, 1) > 0)
{
printf ( "setTTYParity() error/n" ) ;
return - 1;
}
//
idx = 0;
while ( 1)
{
cc[ 0] = 0xFA;
sendnTTY( ptty, & cc[ 0] , 1) ;
nbyte = recvnTTY( ptty, cc, 1) ;
printf ( "%d:%02X/n" , idx+ + , cc[ 0] ) ;
}
cleanTTY( ptty) ;
}
# endif