在linux中,所有的设备文件一般位于"/dev"目录下,其中串口对应的名称为"/dev/ttySx",(因驱动不同,该设备名也会有所不同),可也查看在"/dev"下的文件以确认。在linux下对设备的操作方法与对文件操作方法一样,因此,对串口的读写就可以使用简单的read,write等函数来完成,所不同的只是需要对串口的其他参数另做配置。
串口的设置主要是设置structtermios结构体中的成员,如下所示.
struct termios
{
tcflag_t c_iflag; //输入模式标志
tcflag_t c_oflag; //输出模式标志
tcflag_t c_cflag; //控制模式标志
tcflag_t c_lflag; //本地模式标志
cc_t c_line; //线路规则
cc_t c_cc[NCC]; //控制特性
}
C_iflag支持的常量名称如下表所示,其用于控制端口接收端的字符输入处理。
键 值 |
说 明 |
IGNBRK |
忽略BREAK键输入 |
BRKINT |
如果设置了IGNBRK,BREAK键的输入将被忽略,如果设置了BRKINT ,将产生SIGINT中断 |
IGNPAR |
忽略奇偶校验错误 |
PARMRK |
标识奇偶校验错误 |
INPCK |
允许输入奇偶校验 |
ISTRIP |
去除字符的第8个比特 |
INLCR |
将输入的NL(换行)转换成CR(回车) |
IGNCR |
忽略输入的回车 |
ICRNL |
将输入的回车转化成换行(如果IGNCR未设置的情况下) |
IUCLC |
将输入的大写字符转换成小写字符(非POSIX) |
IXON |
允许输入时对XON/XOFF流进行控制 |
IXANY |
输入任何字符将重启停止的输出 |
IXOFF |
允许输入时对XON/XOFF流进行控制 |
IMAXBEL |
当输入队列满的时候开始响铃,Linux在使用该参数而是认为该参数总是已经设置 |
C_oflag支持的常量名称如下表所示,其用于控制终端端口发送出去的字符处理。
键 值 |
说 明 |
OPOST |
处理后输出 |
OLCUC |
将输入的小写字符转换成大写字符(非POSIX) |
ONLCR |
将输入的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL |
将输入的CR(回车)转换成NL(换行) |
ONOCR |
第一行不输出回车符 |
ONLRET |
不输出回车符 |
OFILL |
发送填充字符以延迟终端输出 |
OFDEL |
以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符将是NUL(‘\0’)(非POSIX) |
NLDLY |
换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY |
回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY |
水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY |
空格输出延迟,可以取BS0或BS1 |
VTDLY |
垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY |
换页延迟,可以取FF0或FF1 |
C_cflag支持的常量名称如下表所示,其是结构中最重要的一个,通过对他的赋值,用户可以设置波特率,字符大小,数据位,停止位,奇偶校验位和硬件流控等。但是c_cflag中的成员不能直接对其初始化,需要将其通过“或”、“与”操作使用其中的某些选项。
键 值 |
说 明 |
CBAUD |
波特率(4+1位)(非POSIX) |
CBAUDEX |
附加波特率(1位)(非POSIX) |
CSIZE |
字符长度,取值范围为CS5、CS6、CS7或CS8 |
CSTOPB |
设置两个停止位 |
CREAD |
使用接收器 |
PARENB |
使用奇偶校验 |
PARODD |
对输入使用奇偶校验,对输出使用偶校验 |
HUPCL |
关闭设备时挂起 |
CLOCAL |
忽略调制解调器线路状态 |
CRTSCTS |
使用RTS/CTS流控制 |
C_lflag支持的常量名称如下表所示,其用于控制终端的本地数据处理和工作模式。
键 值 |
说 明 |
ISIG |
当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号 |
ICANON |
使用标准输入模式 |
XCASE |
在ICANON和XCASE同时设置的情况下,终端只使用大写。如果只设置了XCASE,则输入字符将被转换为小写字符,除非字符使用了转义字符(非POSIX,且Linux不支持该参数) |
ECHO |
显示输入字符 |
ECHOE |
如果ICANON同时设置,ERASE将删除输入的字符,WERASE将删除输入的单词 |
ECHOK |
如果ICANON同时设置,KILL将删除当前行 |
ECHONL |
如果ICANON同时设置,即使ECHO没有设置依然显示换行符 |
ECHOPRT |
如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX) |
TOSTOP |
向后台输出发送SIGTTOU信号 |
C_cc[nccs]支持的常量名称如下表所示
宏 |
说 明 |
宏 |
说 明 |
VINTR |
Interrupt字符 ,对应键 “ctrl+c” |
VEOL |
附加的End-of-file字符 |
VQUIT |
Quit字符 ,”ctrl+z” |
VTIME |
非规范模式读取时的超时时间 |
VERASE |
Erase字符 , “backspace” |
VSTOP |
Stop字符 |
VKILL |
Kill字符 ,”ctrl+u” |
VSTART |
Start字符 |
VEOF |
End-of-file字符 ,”ctrl+d” |
VSUSP |
Suspend字符 |
VMIN |
非规范模式读取时的最小字符数 |
操作串口的控制函数
函数名 |
说明 |
Tcgetattr |
获取属性(termios结构) |
Tcsetattr |
设置属性(termios结构) |
Tcsendbreak |
发送break字符 |
Tcdrain |
等待所有输出都被传输(使程序阻塞,直到输出缓冲区中的数据全部发送完毕) |
tcflow |
挂起传输或接收(用于暂停或重新开始输出) |
Tcflush |
用于清空输入/输出缓冲区 |
cfmakeraw |
设置串口模式为原始模式 |
Cfgetispeed |
获取输入波特率 |
Cfgetospeed |
获取输出波特率 |
Cfsetispeed |
设置输入波特率 |
Cfsetospeed |
设置输出波特率 |
cfsetspeed |
设置波特率 |
Linux串口实现-uart.c
/*************************************************************************************************************
* 文件名: uart.c
* 功能: Linux串口操作函数
* 作者: [email protected]
* 创建时间: 2014年12月31日
* 最后修改时间: 2014年12月31日
* 详细: 无
*************************************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int SerialPortFd = 0; //保存串口的文件描述符
/*************************************************************************************************************************
*函数 : int SerialIsValid(void)
*功能 : 检查串口文件描述符的有效性
*参数 : 无
*返回 : 0:有效
-1:无效
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
static int SerialIsValid(void)
{
//SerialPortFd 安全检查
if(0 == SerialPortFd)
{
perror("SerialPortFd is invalid");
return -1;
}
return 0;
}
/*************************************************************************************************************************
*函数 : int SerialIsValid(void)
*功能 : 检查串口文件描述符的有效性
*参数 : 无
*返回 : 0:有效
-1:无效
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
int SerialOpen(const char *devicePath)
{
SerialPortFd = open(devicePath,O_RDWR | O_NOCTTY | O_NDELAY);
if(SerialPortFd < 0)
{
perror(devicePath);
printf("%s open failed\n",devicePath);
return -1;
}
return 0;
}
/*************************************************************************************************************************
*函数 : int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop)
*功能 : 串口参数设置
*参数 : nSpeed:波特率
nBits :数据位
nEvent:奇偶校验位
nStop :停止位
*返回 : 0:设置成功
-1:设置失败
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop)
{
int rtn = 0;
int speed = 0;
struct termios NewConfig,OldConfig;
//SerialPortFd 安全检查
if(SerialIsValid())
return -1;
//1、保存原串口配置
/*tcgetattr(serialPortFd,&options)得到与SerialFd指向对象的相关参数,
并将它们保存于oldtio,该函数,还可以测试配置是否正确,该串口是否
可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.*/
rtn = tcgetattr(SerialPortFd,&OldConfig);
if(0 != rtn)
{
perror("Error on tcgetattr");
return -1;
}
bzero(&NewConfig,sizeof(NewConfig));
//2、激活选项
//CLOCAL:修改控制模式,保证程序不会占用串口
//CREAD:修改控制模式,接收使能,使得能够从串口中读取输入数据
NewConfig.c_cflag |= CLOCAL | CREAD;
//3、设置数据位
//在设置数据位时,需要先使用CSIZE位清空数据位设置,然后再设置相应的数据位
NewConfig.c_cflag &= ~CSIZE;
switch(nBits)
{
case 8: NewConfig.c_cflag |= CS8;break;
case 7: NewConfig.c_cflag |= CS7;break;
case 6: NewConfig.c_cflag |= CS6;break;
case 5: NewConfig.c_cflag |= CS5;break;
default:
printf("Error on nBits\n");
return -1;
}
//4、设置奇偶校验位
//设置奇偶校验需要使用到termiso中的两个成员:c_cflag和c_iflag,
//首先激活c_cflag中的校验位使能标志PARENB,这样会对输出数据产生校验位,
//而输入数据进行校验检测。同时还要激活c_iflag中的对于输入数据的奇偶校验使能INPCK
//c_cflag中的PARODD为1时使用奇校验,为0时使用偶校验
switch(nEvent)
{
case 'o':
case 'O': //设置为奇校验
NewConfig.c_cflag |=(PARODD|PARENB);
NewConfig.c_iflag |= INPCK;
break;
case 'e':
case 'E': //设置为偶校验
NewConfig.c_cflag |=PARENB;
NewConfig.c_cflag &= ~PARODD;
NewConfig.c_iflag |= INPCK;
break;
case 's':
case 'S': /*as no parity*/
NewConfig.c_cflag &=~PARENB;
NewConfig.c_cflag &= ~CSTOPB;
break;
case 'n':
case 'N': //设置无奇偶校验位
NewConfig.c_cflag &= ~PARENB;
NewConfig.c_iflag &= ~INPCK;
break;
default:
printf("Error on nEvent\n");
return -1;
}
//5、设置波特率
switch(nSpeed)
{
case 2400: speed = B2400; break;
case 4800: speed = B4800; break;
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 115200: speed = B115200; break;
default:
printf("Error on nSpeed\n");
return -1;
}
cfsetispeed(&NewConfig,speed);
cfsetospeed(&NewConfig,speed);
//6、设置停止位
//设置停止位是通过激活c_cflag中的CSTOPB而实现的。若停止位为1个,这清除CSTOPB,若停止位为2个,则激活CSTOPB
switch(nStop)
{
case 1: NewConfig.c_cflag &= ~CSTOPB;break;
case 2: NewConfig.c_cflag |= CSTOPB;break;
default:
printf("Error on nStop\n");
return;
}
//7、设置等待时间和最小接收字符
NewConfig.c_cc[VTIME] = 0;//读取一个字符等待1*(1/10)s
NewConfig.c_cc[VMIN] = 0;//读取字符的最少个数
//8、清除串口缓冲
//在串口重新设置后,需要对当前的串口设备进行适当的处理,
//这时可以调用tcdrain,tcflow,tcflush等函数来处理目前串口缓冲区中的数据
//tcdrain 函数使程序阻塞,直到输出缓冲区中的数据全部发送完毕
//tcflow 函数用于暂停或重新开始输出
//tcflush 函数用于清空输入/输出缓冲区
//如果发生数据溢出,接收数据,但是不再读取
tcflush(SerialPortFd,TCIFLUSH);
//9、激活配置
//在串口配置完成后,需要使用tcsetattr函数激活配置。
rtn = tcsetattr(SerialPortFd,TCSANOW,&NewConfig);
if(0 != rtn)
{
perror("Error on tcsetattr");
return -1;
}
printf("Serial Set Done\n");
return 0;
}
/*************************************************************************************************************************
*函数 : int SerialReceive(unsigned char *buf,int size)
*功能 : 串口接收数据
*参数 : buf: 接收缓冲区
size:缓冲区大小
*返回 :错误-1,返回接收数据的大小
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
int SerialReceive(unsigned char *buf,int size)
{
int rtn ;
//SerialPortFd 安全检查
if(SerialIsValid())
return -1;
rtn = read(SerialPortFd,buf,size);
// printf("ReadLength = %d\n",rtn);
return rtn;
}
/*************************************************************************************************************************
*函数 : int SerialSend(unsigned char *buf,int DataLength)
*功能 : 串口发送数据
*参数 : buf:发送缓冲区
DataLength:需要发送的字节数
*返回 : rtn:成功发送的字节数
-1:发送失败
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
int SerialSend(unsigned char *buf,int DataLength)
{
int rtn ;
//SerialPortFd 安全检查
if(SerialIsValid())
return -1;
#if 0
int remain = DataLength;
int offset = 0;
int sub = 0;
while(remain > 0 )
{
sub = (remain >= 8? 8:remain);
write(SerialPortFd,buf+offset,sub);
tcflush(SerialPortFd,TCOFLUSH);
remain -= 8;
offset += 8;
}
#else
rtn = write(SerialPortFd,buf,DataLength);
#endif
return rtn;
}
/*************************************************************************************************************************
*函数 : int SerialIsValid(void)
*功能 : 关闭串口
*参数 : 无
*返回 : 0:关闭成功
*依赖 : 无
*作者 : edward
*时间 : 20141231
*最后修改时间: 20141231
*说明 : 无
*************************************************************************************************************************/
int SerialClose(void)
{
//SerialPortFd 安全检查
if(SerialIsValid())
return -1;
close(SerialPortFd);
return 0;
}
uart.h头文件
#ifndef __UART_H__
#define __UART_H__
int SerialOpen(const char *devicePath);
int SerialConfig(int nSpeed,int nBits,char nEvent,int nStop);
int SerialReceive(unsigned char *buf,int size);
int SerialSend(unsigned char *buf,int DataLength);
int SerialClose(void);
#endif
#include
#include
#include "uart.h"
int main(int argc,char**argv)
{
int rtn;
int Count = 0;
unsigned char *send = "Hello Uart";
unsigned char buf[1024] ={0};
if(2 > argc){
printf("Input error\n");
printf("Usage: %s \n",argv[0]);
printf("Eample: %s /dev/ttyS0\n",argv[0]);
return -1;
}
//打开串口
rtn = SerialOpen(argv[1]);
if(0 > rtn)
return -1;
//配置串口
rtn = SerialConfig(115200,8,'n',1);
if(0 > rtn)
return -1;
//循环发送与接收
while(1)
{ //发送数据
rtn = SerialSend(send,strlen(send));
if(0 < rtn){
printf("[Send]: %s\n",send);
}
sleep(1);
//接收数据
rtn = SerialReceive(buf,sizeof(buf));
if(0 < rtn){
printf("[Recv]: %s\n",buf);
}
}
}