Linux 串口读写
串口简介
串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使
用。常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年
由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生
产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和
数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用
一个 25 个脚的 DB25 连接器,对连接器的每个引脚的信号内容加以规定,还
对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下,传输电
缆长度应为 50 英尺。
Linux 操作系统从一开始就对串行口提供了很好的支持
流向
符号
功能
TXD DTE→DCEDTE 发送串行数据
RXD DTE←DCEDTE 接收串行数据
RTS DTE→DCEDTE 请求 DCE 将线路切换
CTS DTE←DCEDCE 告诉 DTE 线路已接通
到发送方式
序号
计算机串口的引脚说明
信号名称
发送数据
接收数据
2
3
4
5
6
7
8
20
22
请求发送
允许发送
信号地
载波检测
振铃指示
可以发送数据
数据设备准备好 DSR DTE←DCEDCE 准备好
DCD DTE←DCE表示 DCE 接收到远程载波
信号公共地
数据终端准备好 DTR DTE→DCEDTE 准备好
RI DTE←DCE表示 DCE 与线路接通,出现
振铃
串口操作
串口操作需要的头文件
#include
#include
#include
#include
/*标准输入输出定义*/
/*标准函数库定义*/
/*Unix 标准函数定义*/
/*数据类型,比如一些 XXX_t 的那种*/
#include
#include
#include
#include
/*定义了一些返回值的结构,没看明白*/
/*文件控制定义*/
/*PPSIX 终端控制定义*/
/*错误号定义*/
打开串口
在 Linux 下串口文件是位于 /dev 下的
串口一 为 /dev/ttyS0
串口二 为 /dev/ttyS1
打开串口是通过使用标准的文件打开函数操作:
int fd;
/*以读写方式打开串口*/
fd = open( "/dev/ttyS0", O_RDWR);
if (-1 == fd){
/* 不能打开串口一*/
perror(" 提示错误!");
}
设置串口
最基本的设置串口包括波特率设置,效验位和停止位设置。
串口的设置主要是设置 struct termios 结构体的各成员值。
struct termio
{
unsigned short c_iflag; /* 输入模式标志 */
unsigned short c_oflag;
unsigned short c_cflag;
unsigned short c_lflag;
unsigned char
c_line;
unsigned char
c_cc[NCC];
/* 输出模式标志 */
/* 控制模式标志*/
/* local mode flags */
/* line discipline */
/* control characters */
};
设置这个结构体很复杂,我这里就只说说常见的一些设置:
波特率设置
下面是修改波特率的代码:
struct termios Opt;
tcgetattr(fd, &Opt); /*获得当前设备模式,与终端相关的参数。fd=0
标准输入*/
cfsetispeed(&Opt,B19200); /*设置结构 termios 输入波特率为
19200Bps*/
cfsetospeed(&Opt,B19200);
/*fd 应该是文件描述的意思*/
tcsetattr(fd,TCANOW,&Opt);/*设置终端参数,TCANOW 修改立即发生*/
设置波特率的例子函数:
/**
*@brief 设置串口通信速率
*@param fd
类型 int 打开串口的文件句柄
*@param speed 类型 int 串口速度
*@return void
*/
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400,
B1200, B300,
B38400, B19200, B9600, B4800, B2400,
B1200, B300, };
int name_arr[] =
{38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
void set_speed(int fd, int speed){
19200, 9600, 4800, 2400, 1200, 300, };
int
i;
int
status;
struct termios
Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i]) {
/**
* tcflush 函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚
未读)或输出缓存(用户程序已经写,但尚未发送)。queue 参数应是下列三个常
数之一:
* TCIFLUSH 刷清输入队列。
* TCOFLUSH 刷清输出队列。
* TCIOFLUSH 刷清输入、输出队列。
*/
tcflush(fd, TCIOFLUSH);//设置前 flush
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
//通过 tcsetattr 函数把新的属性设置到串口上。
//tcsetattr(串口描述符,立即使用或者其他标示,指向 termios 的指
针)
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
{
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH); //设置后 flush
}
}
}
效验位和停止位的设置:
无效验
8 位
奇效验(Odd)
7 位
偶效验(Even)
7 位
Option.c_cflag &= ~PARENB;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS8;
Option.c_cflag |= ~PARENB;
Option.c_cflag &= ~PARODD;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS7;
Option.c_cflag &= ~PARENB;
Option.c_cflag |= ~PARODD;
Space 效验
7 位
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS7;
Option.c_cflag &= ~PARENB;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= &~CSIZE;
Option.c_cflag |= CS8;
设置效验的函数:
/**
*@brief 设置串口数据位,停止位和效验位
*@param fd
类型 int 打开的串口文件句柄
*@param databits 类型 int 数据位 取值 为 7 或者 8
*@param stopbits 类型 int 停止位 取值为 1 或者 2
*@param parity 类型 int 效验类型 取值为 N,E,O,,S
*/
int set_Parity(int fd, int databits, int stopbits, int parity)
{
struct termios options;
if (tcgetattr(fd, &options) != 0)
{
perror("SetupSerial 1");
return (FALSE);
}
options.c_cflag &= ~CSIZE;
switch (databits)
/*设置数据位数*/
{
case 7:
case 8:
options.c_cflag |= CS7;
break;
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return (FALSE);
}
switch (parity)
*/
*/
*/
*/
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验
options.c_iflag |= INPCK; /* Disnable parity checking
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
case 2:
options.c_cflag &= ~CSTOPB;
break;
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
options.c_iflag |= INPCK;
}
/* Set input parity option */
if (parity != 'n')
tcflush(fd, TCIFLUSH);
options.c_cc[VTIME] = 150; /* 设置超时 15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it
if (tcsetattr(fd, TCSANOW, &options) != 0)
{
NOW */
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
需要注意的是:
如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用
原始模式(Raw Mode)方式来通讯,设置方式如下:
options.c_lflag &= ~(ICANON | ECHO | ECHOE |
ISIG); /*Input*/
options.c_oflag &= ~OPOST;
/*Output*/
读写串口
设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。
发送数据
char buffer[1024];
int
int
nByte = write(fd, buffer ,Length)
Length;
nByte;
读取串口数据
使用文件操作 read 函数读取,如果设置为原始模式(Raw Mode)传输
数据,那么 read 函数返回的字符数是实际串口收到的字符数。
可以使用操作文件的函数来实现异步读取,如 fcntl,或者 select 等来
操作。
char buff[1024];
int
int readByte = read(fd,buff,Len);
Len;
关闭串口
关闭串口就是关闭文件。
close(fd);
下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件
/********************************************************************
**
* 代码说明:使用串口二测试的,发送的数据是字符,但是没有发送字符串结
束符号,
* 所以接收到后,后面加上了结束符号。我测试使用的是单片机发送数据到第
二个串口,测试通过。
*******************************************************************
-1
0
***/
#define FALSE
#define TRUE
/********************************************************************
*/
int OpenDev(char *Dev)
{
//Dev 就是设备,设备就是文件,就是给出该设备文件的路径
int fd = open(Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY
if (-1 == fd)
{
perror("Can't Open Serial Port");
return -1;
}
else
return fd;
}
int main(int argc, char **argv)
{
int fd;
int nread;
char buff[512];
char *dev = "/dev/ttyS1"; //串口二
fd = OpenDev(dev);
set_speed(fd, 19200);
if (set_Parity(fd, 8, 1, 'N') == FALSE)
{