Linux 串口编程

来源:互联网 发布:怎么把照片导入mac 编辑:程序博客网 时间:2024/06/05 09:17

在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串口操作函数 * 作者:      oxp_edward@163.com  * 创建时间:    2014年12月31日  * 最后修改时间:2014年12月31日   * 详细:      无*************************************************************************************************************/  #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 <strings.h>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个,则激活CSTOPBswitch(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)sNewConfig.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 0int 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;}#elsertn = write(SerialPortFd,buf,DataLength);#endifreturn 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

uart-test测试文件,固定发送字符,并接收,硬件上将串口的RX与TX相连接

#include <stdio.h>#include <string.h>#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 <Device>\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);}}}







0 0