storysnail的Linux串口编程笔记

          storysnail的Linux串口编程笔记

  Author       : He YiJun  (storysnail<at>gmail.com QQ:363559089)
  Develop Team : 7fane Team (http://www.7fane.com,测试网站,到2013年11月底)
  Editor       : Yang QiuXi
  Art Designer : He YiJun
  License      : 7fane Team  License 1.0
  Last Update  : 2013-03-26 

    这半个月因肺部感染而不得不暂时终止那令人生厌的中石油巡检工作,闭门在家安静的
修养。整月的工钱自然是泡汤了,可却得来了极其珍贵的个人闲暇时光,让我能淋漓尽致的
做软件方面的研究,虽是粗茶淡饭,针剂苦药,但可静心埋头于书房,却比天堂还甜美!

    恍惚已至月末,工作单位来了音讯,让我一下子从甜美的梦中惊醒,从哪里来,回哪里
去,这种如"主体思想"一样可怕的思维是我挥之不去的梦魇,无奈、不知所措、病弱的身体
却不由自主的向那发声的地方靠去!

    好了,还是不再发牢骚了,只是个人觉得这种臃肿低效的国企能够存在,本身就是对“
国富论”绝佳的嘲讽,我只能用世界是多元的来啊Q一下了!

    切入正题,这段时间做GSM/GPRG和GPS的小东西,需要通过串口发送AT指令来控制,以前
调试一直在用串口助手和minicom之类的现成软件,可是一点都不爽,为什么不自己写个操作
串口的软件,就像在ARM和stm32上一样!

    这文章其实只是我的一个笔记,分为两篇,一篇是《storysnail的Windows串口编程笔记》,
另一篇是《storysnail的Linux串口编程笔记》,由于网上已经有非常多的类似文章,有些长篇
大论,有些短小精悍,连我自己都思考过是否有必要再写一篇,但在Ling的鼓动下还是写了!

    本篇是Linux串口编程笔记,详细介绍了串口通信会用到的api函数,并提供了一个示例程序,
这个示例程序是在EEEPC701的debian系统上编写测试的。

一:写串口程序用到的函数
  1:Linux与windows串口设备文件名对照

操作系统       串口1        串口2        USB/RS-232转换器
Windows        COM1        COM2         COMX(我的系统上X=4)
Linux        /dev/ttyS0    /dev/ttyS1    /dev/ttyUSB0 

  2:写串口程序用到的函数
    串行通讯函数定义在termios.h头文件中,所以需要包含该文件。下面是要介绍的函数列表

open         打开串口 
close        关闭串口
read         接收数据
write        发送数据
fcntl        设置IO为阻塞或非阻塞
ioctl        实现POSIX.1 GTI控制界面所有函数功能
tcgetattr    读取串口设备的当前属性,保存在termios_p所指向的结构中
tcsetattr    设置串口设备的当前属性
cfgetospeed  返回输出波特率
cfgetispeed  返回输入波特率
cfsetispeed  设定输入波特率
cfsetospeed  设定输出波特率 

2.1
open
用途:打开串口
原型:int open( const char * pathname,int flags);
参数说明:
pathname:   指向欲打开的文件路径字符串
flags       所能使用的标志位:
                O_RDONLY   以只读方式打开文件
                O_WRONLY   以只写方式打开文件
                O_RDWR     以可读写方式打开文件。
                           O_RDONLY、O_WRONLY、O_RDWR标志位是互斥的,不可同时使用,但可与下列的
                           标志位|运算组合。
                O_CREAT    若欲打开的文件不存在则自动建立该文件。
                O_EXCL     如果O_CREAT 也被设置,此指令会去检查文件是否存在。文件若不存在则建立该文件,
                           否则将导致打开文件错误。此外,若O_CREAT与O_EXCL同时设置,并且欲打开的文件为
                           符号连接,则会打开文件失败。
                O_NOCTTY   表明本程序不是该串口上的“控制终端”。即本程序不受Ctrl+c、Ctrl+z这类
                           组合键产生的信号影响。
                O_TRUNC    若文件存在并且以可写的方式打开时,此标志位会令文件长度清为0,
                           而原来存于该文件的 资料也会消失。
                O_APPEND   当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。
                O_NONBLOCK 非阻塞模式打开。在打开很多串行端口设备时,open函数有时候会阻塞很长一段
                           时间.例如当打开一个调制解调器的端口就会阻塞直到DCD信号线有信号电压为止,
                           如果串口的另一端没有连接任何设备,那么DCD信号线上就不会有信号电压,这会
                           导致open函数一直阻塞在那里等待DCD信号,导致程序失去响应。使用该选项
                           程序会忽略DCD信号线上的信号。所以为了无阻塞地打开一个文件但不影响正常的
                           阻塞IO,必须先用O_NONBLOCK选项调用open函数,然后使用fcntl切换到非阻塞IO
                           状态。
                O_NDELAY   其实和O_NONBLOCK基本相同,所产生的结果都是使I/O变成非阻塞模式,唯一的
                           一点差别是O_NDELAY会让函数马上返回0,而O_NONBLOCK在读不到数据时会返回-1,
                           并且设置errno为EAGAIN。在GNU C中O_NDELAY只是为了与BSD的程序兼容,实际上
                           在fcntl.h中是使用O_NONBLOCK作为宏定义,所以建议现在使用O_NONBLOCK.
                             #define O_NDELAY O_NONBLOCK
                O_SYNC     以同步的方式打开文件。
                O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接,则会令打开文件失败。
                O_DIRECTORY如果参数pathname 所指的文件并非为一目录,则会令打开文件失败。

特别说明:
            关于FNDELAY
            还有一个FNDELAY,实际上在fcntl.h中是使用O_NDELAY作为宏定义.
              #define FNDELAY O_NDELAY
举例:
         

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <errno.h>
#include <termios.h> 
 
int Com_Open(void)
{
  int fd = -1;

  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK);
  if(fd == -1) {
    perror("open_port: Unable to open /dev/ttyUSB0");
  }

  if(Com_SetIOBlockFlag(fd,BLOCK_IO) == -1)
    printf("IO set error\n");


  return (fd);
}

2.2             
close       
用途:关闭串口             
原型:int close( int fd);
参数说明:
fd:  文件描述符,关闭串口后计算机会将DTR信号线设置成低电位,这会告诉另一端的设备你的计算机状态。
举例:

int Com_Close(int fd)
{
  if (fd < 0)
    return -1;

  if (close(fd) == -1)
    return -1;

  printf("close uart\n\n");

  return 0;
}

2.3
read        
用途:接收数据
原型:ssize_t read(int fd, void *buf, ssize_t nbyte);
参数说明:
fd:     文件描述符
buffer:读取缓冲区
number:要读多少个字节,不能大于buf指向的缓冲区大小
举例:

n = read(fd,buf,sizeof(buf)); 

2.4
write       
用途:发送数据
原型:ssize_t write (int fd,const void * buf,size_t count);
参数说明:
fd:  文件描述符
buffer:写入缓冲区
count:要写多少个字节
    write函数也会返回发送数据的字节数或者在发生错误的时候返回-1。
通常,发送数据最常见的错误就是EIO,当调制解调器或者数据链路将Data Carrier Detect(DCD)
信号线弄掉了,就会发生这个错误。而且,直至关闭端口这个情况会一直持续。
举例:

n = write(fd, "AT\r", 3);
if (n < 0)
  perror("write() of 4 bytes failed!\n", stderr); 

2.5 
fcntl       
用途:设置IO为阻塞或非阻塞
原型:int fcntl(int fd,int cmd,...);
参数说明:略
举例:
#define BLOCK_IO    0
#define NONBLOCK_IO 1

int Com_SetIOBlockFlag(int fd,int value)
{
  int oldflags;

  if (fd == -1)
    return -1;

  oldflags = fcntl(fd,F_GETFL,0);
  if(oldflags == -1) {
    printf("get IO flags error\n");
    return -1;
  }

  if(value == BLOCK_IO)
    oldflags &= ~O_NONBLOCK;  //设置成阻塞IO
  else
    oldflags |= O_NONBLOCK;   //设置成非阻塞IO

  return fcntl(fd,F_GETFL,oldflags);
}

2.6
ioctl       
用途:实现POSIX.1 GTI控制界面所有函数功能,配置串口不仅仅可以使用上面说的方法,
      在Linux环境下,还可以使用ioctl系统调用来实现,写过驱动程序的人都知道,
      ioctl是个大口袋,任何IO操作都可以交给它。
原型:int ioctl(int fd, int request, ...); 
参数说明:
fd:       串口设备文件的文件描述符。
request:  参数在asm-generic/ioctl.h头文件中定义
            串口设置
            TCGETS        读取当前的串口属性   
                        同功能函数:tcgetattr
            TCSETS        设置串口属性并立即生效   
                        同功能函数:tcsetattr(fd, TCSANOW, &options)
            TCSETSF        设置串口属性,等到输入输出队列都清空了再生效   
                        同功能函数:tcsetattr(fd, TCSAFLUSH, &options)
            TCSETSW        设置串口属性,等到输入输出队列都清空或传输完成了再生效
                        同功能函数:tcsetattr(fd, TCSADRAIN, &options)
            TCSBRK        在指定时间后发送break   
                        同功能函数:tcsendbreak
            TCXONC      控制软件流控制
                        同功能函数:tcflow
            TCFLSH        丢弃输入输出队列中尚未传送或读取的数据!
                        同功能函数:tcflush
                        注意:tcflush这个函数的命名就是个灾难,因为"flush"在linux中用于
                        描述“等待直至所有输入输出全部传送完毕”,例如:fflush
            FIONREAD    返回输入队列中的字节数

            这4个IO控制命令用于获取和设置MODEM握手,如RTS、CTS、DTR、DSR、RI、CD等,不过有些嵌入式设备的uart
            并不包括完整的MODEM控制信号线       
            TIOCMGET    获取MODEM状态位
            TIOCMSET    设置MODEM状态位
            TIOCMBIC    清除指示MODEM的位
            TIOCMBIS    设置指示MODEM的位

            获得串口控制信号
            TIOCM_LE    DSR (data set ready/line enable)
            TIOCM_DTR    DTR (data terminal ready)
            TIOCM_RTS    RTS (request to send)
            TIOCM_ST    Secondary TXD (transmit)
            TIOCM_SR    Secondary RXD (receive)
            TIOCM_CTS    CTS (clear to send)
            TIOCM_CAR    DCD (data carrier detect)
            TIOCM_CD    Synonym for TIOCM_CAR
            TIOCM_RNG    RNG (ring)
            TIOCM_RI    Synonym for TIOCM_RNG
            TIOCM_DSR    DSR (data set ready)
举例:

//获取MODEM状态位
int fd;
int status;
ioctl(fd, TIOCMGET, &status);
//设置MODEM状态位,将DTR信号线设成掉线状态。
int fd;
int status;
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_DTR;
ioctl(fd, TIOCMSET, &status); 

//得到串口输入队列中的字节数
int fd;
int bytes;
ioctl(fd, FIONREAD, &bytes);  

2.7
tcgetattr()
用途:读取串口设备的当前属性,保存在termios_p所指向的结构中
原型:int tcgetattr (int fd, struct termios *termios_p);
参数说明:
fd:        串口设备描述符
termios_p: termios结构体指针

2.8
tcsetattr()
用途:设置串口设备的当前属性
原型:int tcsetattr (int fd, int action,const struct termios *termios_p);
参数说明:
fd:        串口设备描述符
action:    TCSANOW        立即做出改变   
           TCSADRAIN    等到输入输出队列都清空了再生效   
           TCSAFLUSH    等到输入输出队列都清空或传输完成了再生效
termios_p: termios结构体指针         
2.9             
cfgetospeed()
用途:返回输出波特率
原型:speed_t cfgetospeed (const struct termios *termios_p);
参数说明:
termios_p: termios结构体指针

2.10
cfgetispeed()
用途:返回输入波特率
原型:speed_t cfgetispeed (const struct termios *termios_p);
参数说明:
termios_p: termios结构体指针

2.11
cfsetispeed()
用途:设定输入波特率
原型:int cfsetispeed (struct termios *termios_p, speed_t speed);
termios_p: termios结构体指针
speed: 见cfsetispeed()函数

2.12
cfsetospeed()
用途:设定输出波特率
原型:int cfsetospeed (struct termios *termios_p, speed_t speed);
termios_p: termios结构体指针
speed:     详见bits/termios.h头文件,这里只列出几个常用值
      #define  B1200    0000011
      #define  B1800    0000012
      #define  B2400    0000013
      #define  B4800    0000014
      #define  B9600    0000015
      #define  B19200    0000016
      #define  B38400    0000017   
      #define  B57600   0010001
      #define  B115200  0010002
举例:

struct termios options;
speed_t InSpeed,OutSpeed; 

tcgetattr(fd,&options);
InSpeed  = cfgetispeed(&options);
OutSpeed = cfgetospeed(&options); 

//设置输入和输出波特率为115200
cfsetispeed(&options,B115200);
cfsetospeed(&options,B115200); 

tcsetattr(fd,TCSANOW,&options); 

二:配置串口 -- POSIX.1 GTI控制界面
    POSIX.1 GTI定义在termios.h和bits/termios.h头文件中,由一个termios结构体和12个函数构成,这里只介绍
了部分内容,更多内容可参考本文结尾提到的书籍!

  1:termios结构

typedef unsigned char    cc_t;
typedef unsigned int    speed_t;
typedef unsigned int    tcflag_t; 

#define NCCS 32
struct termios
{
    tcflag_t c_iflag;        /* 输入方式标志 */
    tcflag_t c_oflag;        /* 输出方式标志 */
    tcflag_t c_cflag;        /* 控制方式标志 */
    tcflag_t c_lflag;        /* 局部方式标志 */
    cc_t c_line;            /* line discipline */
    cc_t c_cc[NCCS];        /* 控制字符数组 */
    speed_t c_ispeed;        /* 输入波特率 */
    speed_t c_ospeed;        /* 输出波特率 */
}; 

  2:termios结构体的c_cflag成员
    termios结构体的c_cflag描述基本的串口硬件控制,像波特率、是否激活奇偶校验检查、
校验方式、一个字节的数据位个数、停止位个数和硬件流控制等。所有的都是位操作,需要使
用位运算的与或非组合来设置或者清除相关标志。

#define CSIZE    0000060        //一个字节的数据位个数
#define   CS5    0000000        //一个字节的数据位个数为5
#define   CS6    0000020        //一个字节的数据位个数为6
#define   CS7    0000040        //一个字节的数据位个数为7
#define   CS8    0000060        //一个字节的数据位个数为8
#define CSTOPB    0000100        //停止位个数2,否则为1  不理解的参见下面的代码
#define CREAD    0000200        //启用接收器
#define PARENB    0000400        //激活奇偶校验检查
#define PARODD    0001000        //校验方式为奇效验,否则为偶效验
#define HUPCL    0002000        //最后一次关闭时挂断,即将DTR信号线设置成低电位
#define CLOCAL    0004000        //忽略状态线,没有流控制
#define CRTSCTS  020000000000  //启用硬件流控制,注意软件流控是在c_iflag中设置的 
设置一个字节的数据位个数
options.c_flag &= ~CSIZE; /* Mask the character size bits */
options.c_flag |= CS8;    /* Select 8 data bits */ 

设置奇偶校验 

无奇偶校验 (8N1)
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; 

偶校验(7E1)
options.c_cflag |= PARENB
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7; 

奇校验(7O1)
options.c_cflag |= PARENB
options.c_cflag |= PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7; 

Mark校验,通过2位停止位来模拟(7M1)
options.c_cflag &= ~PARENB
options.c_cflag |= CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7; 

Space校验 ,这与无奇偶校验相同(7S1)
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; 

设置硬件流控制
启用硬件流控制
options.c_cflag |= CRTSCTS;    /* Also called CRTSCTS 

停用硬件流控制
options.c_cflag &= ~CRTSCTS; 

  3:termios结构体的c_lflag成员
    termios结构体的c_lflag用来设置串口驱动与用户之间的界面,例如打开还是关闭回显,
是否显示ERASE字符,使用加工方式输入还是使用非加工方式输入。由于我只需要得到原始数据,
所以只需要将termios结构体的c_lflag置0即可。

ISIG    启用 SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT信号
ICANON    启用加工方式输入,否则使用非加工方式输入
XCASE    启用加工大小写
ECHO    启用回显
ECHOE    用BS-SP-BS回显擦写字符(ERASE)
ECHOK    回显KILL字符
ECHONL    回显NL字符
NOFLSH    禁止中断、退出或终止后清除输出队列
IEXTEN    启用实现定义的功能
ECHOCTL    回显控制字符,例如 ^和 ~?
ECHOKE    通过擦去屏幕上的字符回显KILL
FLUSHO    输出被清除
PENDIN    重新打印悬挂的输出
TOSTOP    为后台输出发送SIGTTOU信号

设置非加工方式输入 
    在非加工方式下,原始输入根本不会被处理。输入字符只是被原封不动的接收。
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 

设置加工方式输入
options.c_lflag |= (ICANON | ECHO | ECHOE); 

注意:当发送数据给调制解调器或其它计算机时,如果它们对发送的数据启用回显,
      则发送端千万不要启用回显,那样会导致两个连接的串口之间生成信息反馈循环。

  4:termios结构体的c_oflag成员
    termios结构体的c_oflag用来控制串口的数据输出例如转换换行符CR/LF等,或选择
使用加工方式输出还是使用非加工方式输出。和上面一样,我只需要得到原始数据,所以
只需要将termios结构体的c_oflag置0即可。

OPOST    启用加工方式输出,否则使用非加工方式输出
OLCUC    转换输出时的小写字符为大写字符
ONLCR    将换行字符(NL)转换成回车换行字符(CR-NL)
OCRNL    将回车字符(CR)转换成换行字符(NL)
NOCR    No CR output at column 0
ONLRET    NL performs CR function
OFILL    Use fill characters for delay
OFDEL    Fill character is DEL
NLDLY    Mask for delay time needed between lines
NL0        No delay for NLs
NL1        Delay further output after newline for 100 milliseconds
CRDLY    Mask for delay time needed to return carriage to left column
CR0        No delay for CRs
CR1        Delay after CRs depending on current column position
CR2        Delay 100 milliseconds after sending CRs
CR3        Delay 150 milliseconds after sending CRs
TABDLY    Mask for delay time needed after TABs
TAB0    No delay for TABs
TAB1    Delay after TABs depending on current column position
TAB2    Delay 100 milliseconds after sending TABs
TAB3    Expand TAB characters to spaces
BSDLY    Mask for delay time needed after BSs
BS0     No delay for BSs
BS1        Delay 50 milliseconds after sending BSs
VTDLY    Mask for delay time needed after VTs
VT0        No delay for VTs
VT1        Delay 2 seconds after sending VTs
FFDLY    Mask for delay time needed after FFs
FF0        No delay for FFs
FF1        Delay 2 seconds after sending FFs

设置成使用非加工方式输出,当然c_oflag中其它选项都会失效
options.c_oflag &= ~OPOST; 

  5:termios结构体的c_iflag成员
    termios结构体的c_iflag用来控制串口的数据输入,例如剥离输入字符为8位,使奇偶效验生效等

c_iflag成员可以使用的常量
常量    描述
INPCK    启用奇偶效验检查
IGNPAR    忽略奇偶效验错误
PARMRK    标识奇偶效验出错的数据
ISTRIP    剥去输入字符至7位
IXON    启用输出软件流控制
IXOFF    启用输入软件流控制
IXANY    启用任何输入字符回复暂停的输出
IGNBRK    忽略输入行的终止条件
BRKINT    当输入行中监测到终止条件时发送SIGINT信号
INLCR    将换行字符(NL)转换成回车字符(CR)
IGNCR    忽略CR
ICRNL    将回车字符(CR)转换成换行字符(NL)
IUCLC    将大写字符转换成小写字符
IMAXBEL    输入队列满时响铃

启用奇偶效验检查并从接收字符串中脱去奇偶校验位:
options.c_iflag |= (INPCK | ISTRIP); 

启用IGNPAR会忽略奇偶效验错误并给数据放行。如果你使用8N1模式,可以考虑开启该功能。 

当启用INPCK后,启用PARMRK会标识奇偶效验出错的数据。设该数据为'X',如果启用IGNPAR,那么一个NUL('\0')字符会被
加入到发生奇偶校验错误的字符前面,即'\0''X'。
否则,DEL('\377',注意这是8进制)和NUL('\0')字符会和出错的字符一起送出,即'\377''\0''X'。 

启用软件流控制
options.c_iflag |= (IXON | IXOFF | IXANY); 

禁用软件流控制
options.c_iflag &= ~(IXON | IXOFF | IXANY); 

  6:termios结构体的c_cc成员
    termios结构体的c_cc里面包括了控制字符的定义和超时参数。
c_cc数组在加工方式和非加工方式下的作用不同,作为c_cc数组索引的宏常量也分为两种情况使用,
并且索引值在两种加工方式下有部分重叠,所以一定要区分对待。

#define VINTR 0
#define VQUIT 1
#define VERASE 2
#define VKILL 3
#define VEOF 4
#define VTIME 5
#define VMIN 6
#define VSWTC 7
#define VSTART 8
#define VSTOP 9
#define VSUSP 10
#define VEOL 11
#define VREPRINT 12
#define VDISCARD 13
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16

c_cc的另一个功能就是设置超时参数,MIN和TIME存储在c_cc数组中,通过VMIN和VTIME引用.
MIN和TIME只在非加工方式下才有实际意义,它们分别用来控制等待多少字节的输入数据到达
以及等待多长时间,单位为0.1秒。
超时设置在下列两种情况下会失效:
在加工方式
使用O_NUNBLOCK参数调用open和fcntl函数,使IO变为非阻塞

对于非加工输入方式,典型的设置如下:
    options.c_cc[VMIN]  = 1;
    options.c_cc[VTIME] = 0; 

下面是MIN和TIME的功能组合表:

            MIN = 0                                 MIN > 0
TIME = 0    read立即返回[0,nbytes]                   当队列中有大于MIN的字节时,read返回[MIN,nbytes]
                                                    否则read会一直阻塞 

TIME > 0    TIME没溢出时,read返回[MIN,nbytes]        TIME没溢出时,read返回[MIN,nbytes]  
            TIME溢出时,read返回[1,MIN]               TIME溢出时,read返回[1,MIN]
            这的TIME是read被阻塞的时间                   这个TIME是队列里接收到的字节间的时间,
                                                            所以调用者可能会被一直阻塞 

三:示例程序
    上面介绍了大部分的串口通信会用到的函数和数据结构,Linux上写串口通讯程序时可以选择
采用多进程,当然也可以使用Pthread的多线程,不过我的示例程序并没有使用这些,和windows
上的示例程序类似,我还是认为这样可以更清晰的展现如何操作串口。

linux001

 

linux002

 

linux003

 

linux004

 

 

 

 

 

/**************************************************************************************************
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
                               main.c 

    Develop Team     :    7fane Team
    Team  Leader     :    He YiJun  (storysnail<at>gmail.com QQ:363559089)
    Main Programmer  :    He YiJun
    Program comments :    Ling Ying
    License          :    7fane Team  License 1.0
    Last Update      :    2013-03-25
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  功能说明: 

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  更    新:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  已知问题:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
**************************************************************************************************/ 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "uart-linux.h" 

#define CMD_MAX_LEN     255 

/**************************************************************************************************
函数名称:ME_Init()
函数功能:设备初始化,其实这个函数只是聋子的耳朵--摆设
函数说明:无
入口参数:fd:串口设备文件描述符
出口参数:成功返回0,失败返回-1
调用实例:无
**************************************************************************************************/
int ME_Init(int fd)
{
  unsigned char ReadBuffer[COM_MAX_BUFFER+1];
  unsigned char WriteBuffer[COM_MAX_BUFFER+1];
  ssize_t rCount = 0;
  ssize_t wCount = 0; 

  while (1) {
    sleep(1); 

    memset(ReadBuffer,'\0',COM_MAX_BUFFER+1);
    rCount = Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
    if(rCount > 0) {
      printf("Read com: %s\n",ReadBuffer);
      printf("Read com char num: %d\n",(int)rCount);
      if((strstr((char *)ReadBuffer,"AT") != NULL) && (strstr((char *)ReadBuffer,"OK") != NULL)) {
        break;
      }
    } 

    sleep(1); 

    memset(WriteBuffer,'\0',COM_MAX_BUFFER+1);
    WriteBuffer[0] = 'A';
    WriteBuffer[1] = 'T';
    WriteBuffer[2] = 0x0d;
    WriteBuffer[3] = '\0';
    wCount = Com_Write(fd,WriteBuffer,strlen((char *)WriteBuffer));
    if(wCount > 0) {
      printf("Wrote com: %s\n",WriteBuffer);
      printf("Wrote com char num: %d\n",(int)wCount);
    }
  } 

  return 0;
} 

/**************************************************************************************************
函数名称: main()
函数功能:main()
函数说明:main()
入口参数:无
出口参数:0
调用实例:无
**************************************************************************************************/
int main()
{
  unsigned char ReadBuffer[COM_MAX_BUFFER+1];
  unsigned char WriteBuffer[COM_MAX_BUFFER+1];
  char cmd[CMD_MAX_LEN+1];
  ssize_t rCount  = 0;
  ssize_t wCount = 0;
  int fd = -1; 

  if((fd = Com_Open()) == -1) {
    return 0;
  } 

  if(Com_Setup(fd,115200, 8, 1, 0, 0) == -1) {
    Com_Close(fd);
    return 0;
  } 

  if(ME_Init(fd) == -1) {
    Com_Close(fd);
    return 0;
  } 

  while (1) {
    memset(cmd,'\0',CMD_MAX_LEN+1);
    printf ("\nEnter Command: ");
    if (!fgets (cmd, CMD_MAX_LEN,stdin)) {
      perror ("fget error");
      exit(1);
    }
    /* Get rid of the new line at the end */
    /* Messages use 8-bit characters */
    cmd[strlen(cmd)-1] = '\0'; 

    if (strcmp (cmd, "$Quit") == 0)
      break; 

    if (strncmp (cmd, "block",sizeof("block")) == 0) {
      if(Com_SetIOBlockFlag(fd,BLOCK_IO) != -1) {
        printf("Set IO block flags success!\n");
      }
      else {
        printf("Set IO block flags error!\n");
      }
    } 

    if (strncmp (cmd, "nonblock",sizeof("nonblock")) == 0) {
      if(Com_SetIOBlockFlag(fd,NONBLOCK_IO) != -1) {
        printf("Set IO no block flags success!\n");
      }
      else {
        printf("Set IO no block flags error!\n");
      }
    } 

    if (strncmp (cmd, "read",sizeof("read")) == 0) {
      memset(ReadBuffer,'\0',COM_MAX_BUFFER+1);
      rCount = Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
      if(rCount > 0) {
        printf("ReadBuffer: %s\n",ReadBuffer);
        printf("Read com char num: %d\n",(int)rCount);
      }
    } 

    if (strncmp (cmd, "write",sizeof("write")) == 0) {
      memset(WriteBuffer,'\0',COM_MAX_BUFFER);
      WriteBuffer[0] = 'A';
      WriteBuffer[1] = 'T';
      WriteBuffer[2] = 0x0d;
      WriteBuffer[3] = '\0';
      printf("WriteBuffer: %s\n",WriteBuffer);
      wCount = Com_Write(fd,WriteBuffer,strlen((char *)WriteBuffer));
      sleep(1);
      memset(ReadBuffer,'\0',COM_MAX_BUFFER+1);
      rCount = Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
      if(rCount > 0) {
        printf("ReadBuffer: %s\n",ReadBuffer);
        printf("Read com char num: %d\n",(int)rCount);
      }
    }
  } 

  Com_Close(fd);
  return 0;
} 

/**************************************************************************************************
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
                               uart-linux.h 

    Develop Team     :    7fane Team
    Team  Leader     :    He YiJun  (storysnail<at>gmail.com QQ:363559089)
    Main Programmer  :    He YiJun
    Program comments :    Ling Ying
    License          :    7fane Team  License 1.0
    Last Update      :    2013-03-25
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  功能说明:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  更    新:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  已知问题:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
**************************************************************************************************/ 

#ifndef __UART_LINUX_H__
#define __UART_LINUX_H__ 

#define UART_MIN(A,B)  ((A) < (B) ? (A):(B)) 

#define COM_MAX_BUFFER   512  //串口数据缓存的最大字节数 

#define BLOCK_IO    0
#define NONBLOCK_IO 1 

extern int Com_Open(void);
extern int Com_Close(int fd);
extern int Com_SetIOBlockFlag(int fd,int value);
extern int Com_GetInQueByteCount(int fd,int *ByteCount);
extern int Com_Setup(int fd,unsigned int baud, int databit, int stopbit, int parity, int flow);
extern int Com_ChangeBaudrate(int fd, unsigned int baud);
extern ssize_t Com_Read(int fd, unsigned char *ReadBuffer, ssize_t ReadSize);
extern ssize_t Com_Write(int fd, unsigned char *WriteBuffer, ssize_t WriteSize); 

#endif 

/**************************************************************************************************
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
                               uart-linux.c 

    Develop Team     :    7fane Team
    Team  Leader     :    He YiJun  (storysnail<at>gmail.com QQ:363559089)
    Main Programmer  :    He YiJun
    Program comments :    Ling Ying
    License          :    7fane Team  License 1.0
    Last Update      :    2013-03-25
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  功能说明:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  更    新:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
  已知问题:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
**************************************************************************************************/ 

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include "uart-linux.h" 

/**************************************************************************************************
函数名称:Com_Open()
函数功能:打开串口
函数说明:无
入口参数:无
出口参数:成功返回串口设备文件描述符,失败返回-1
调用实例:无
**************************************************************************************************/
int Com_Open(void)
{
  int fd = -1; 

  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK);
  if(fd == -1) {
    perror("open_port: Unable to open /dev/ttyUSB0");
  } 

  if(Com_SetIOBlockFlag(fd,BLOCK_IO) == -1)
    printf("IO set error\n"); 

  return (fd);
} 

/**************************************************************************************************
函数名称:Com_Close()
函数功能:关闭串口
函数说明:无
入口参数:fd:串口设备文件描述符
出口参数:无
调用实例:无
**************************************************************************************************/
int Com_Close(int fd)
{
  if (fd < 0)
    return -1; 

  if (close(fd) == -1)
    return -1; 

  printf("close uart\n\n"); 

  return 0;
} 

/**************************************************************************************************
函数名称:Com_SetIOBlockFlag()
函数功能:设置IO为阻塞或非阻塞
函数说明:无
入口参数:fd:串口设备文件描述符  value:BLOCK_IO或NONBLOCK_IO
出口参数:失败返回-1,否则返回其它值
调用实例:无
**************************************************************************************************/
int Com_SetIOBlockFlag(int fd,int value)
{
  int oldflags; 

  if (fd == -1)
    return -1; 

  oldflags = fcntl(fd,F_GETFL,0);
  if(oldflags == -1) {
    printf("get IO flags error\n");
    return -1;
  } 

  if(value == BLOCK_IO)
    oldflags &= ~O_NONBLOCK;  //设置成阻塞IO
  else
    oldflags |= O_NONBLOCK;   //设置成非阻塞IO 

  return fcntl(fd,F_GETFL,oldflags);
} 

/**************************************************************************************************
函数名称:Com_GetInBufSize()
函数功能:得到串口输入队列中的字节数
函数说明:无
入口参数:fd:串口设备文件描述符  InBufSize:串口输入队列中的字节数会保存在该指针所指向的内存
出口参数:失败返回-1,否则返回0
调用实例:无
**************************************************************************************************/
int Com_GetInQueByteCount(int fd,int *ByteCount)
{
  int bytes = 0; 

  if (fd == -1)
    return -1; 

  if(ioctl(fd, FIONREAD, &bytes) != -1) {
    *ByteCount = bytes;
    return 0;
  } 

  return -1;
} 

/**************************************************************************************************
函数名称:Com_Setup()
函数功能:串口设定函数
函数说明:无
入口参数:fd:串口设备文件描述符
           baud:比特率 300、600、1200、2400、4800、9600、19200、38400、57600、115200
           databit:一个字节的数据位个数 5、6、7、8
           stopbit:停止位个数1、2
           parity:奇偶校验 0:无奇偶效验  1:奇效验  2:偶效验
           flow:硬件流控制 0:无流控、 1:软件流控  2:硬件流控
出口参数:失败返回-1,否则返回0
调用实例:无
**************************************************************************************************/
int Com_Setup(int fd,unsigned int baud, int databit, int stopbit, int parity, int flow)
{
  struct termios options; 

  if (fd == -1)
    return -1; 

  if(tcgetattr(fd, &options) == -1)
    return -1; 

      switch (baud) {  //取得比特率
      case 300:
        options.c_cflag =  B300;
        break;
      case 600:
        options.c_cflag =  B600;
        break;
      case 1200:
        options.c_cflag =  B1200;
        break;
      case 2400:
        options.c_cflag =  B2400;
        break;
      case 4800:
        options.c_cflag =  B4800;
        break;
      case 9600:
        options.c_cflag =  B9600;
        break;
      case 19200:
        options.c_cflag =  B19200;
        break;
      case 38400:
        options.c_cflag =  B38400;
        break;
      case 57600:
        options.c_cflag =  B57600;
        break;
      case 115200:
        options.c_cflag =  B115200;
        break;
      default:
        options.c_cflag =  B19200;
        break;
      } 

  switch (databit) {  //取得一个字节的数据位个数
  case 5:
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS5;
    break;
  case 6:
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS6;
    break;
  case 7:
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
    break;
  case 8:
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    break;
  default:
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    break;
  } 

  switch (parity) {  //取得奇偶校验
  case 0:
    options.c_cflag &= ~PARENB;              // 无奇偶效验
    options.c_iflag &= ~(INPCK  | ISTRIP);   // 禁用输入奇偶效验
    options.c_iflag |= IGNPAR;               // 忽略奇偶效验错误
    break;
  case 1:
    options.c_cflag |= (PARENB | PARODD);    // 启用奇偶效验且设置为奇效验
    options.c_iflag |= (INPCK  | ISTRIP);    // 启用奇偶效验检查并从接收字符串中脱去奇偶校验位
    options.c_iflag &= ~IGNPAR;              // 不忽略奇偶效验错误
    break;
  case 2:
    options.c_cflag |= PARENB;               // 启用奇偶效验
    options.c_cflag &= ~PARODD;              // 设置为偶效验
    options.c_iflag |= (INPCK  | ISTRIP);    // 启用奇偶效验检查并从接收字符串中脱去奇偶校验位
    options.c_iflag &= ~IGNPAR;              // 不忽略奇偶效验错误
    break;
  default:
    options.c_cflag &= ~PARENB;              // 无奇偶效验
    options.c_iflag &= ~(INPCK  | ISTRIP);   // 禁用输入奇偶效验
    options.c_iflag |= IGNPAR;               // 忽略奇偶效验错误 

    break;
  } 

  switch (stopbit) {  //取得停止位个数
  case 1:
    options.c_cflag &= ~CSTOPB;               // 一个停止位
    break;
  case 2:
    options.c_cflag |= CSTOPB;                // 2个停止位
    break;
  default:
      options.c_cflag &= ~CSTOPB;               // 默认一个停止位
    break;
  } 

  switch (flow) {  //取得流控制
  case 0:
    options.c_cflag &= ~CRTSCTS;                // 停用硬件流控制
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // 停用软件流控制
    options.c_cflag |= CLOCAL;                  // 不使用流控制
  case 1:
    options.c_cflag &= ~CRTSCTS;                // 停用硬件流控制
    options.c_cflag &= ~CLOCAL;                 // 使用流控制
    options.c_iflag |= (IXON | IXOFF | IXANY);  // 使用软件流控制
    break;
  case 2:
    options.c_cflag &= ~CLOCAL;                 // 使用流控制
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // 停用软件流控制
    options.c_cflag |= CRTSCTS;                 // 使用硬件流控制
    break;
  default:
    options.c_cflag &= ~CRTSCTS;                // 停用硬件流控制
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // 停用软件流控制
    options.c_cflag |= CLOCAL;                  // 不使用流控制
    break;
  } 

  options.c_cflag |= CREAD;                     // 启用接收器
  options.c_iflag |= IGNBRK;                    // 忽略输入行的终止条件
  options.c_oflag = 0;                          // 非加工方式输出
  options.c_lflag = 0;                          // 非加工方式
  //options.c_lflag     &= ~(ICANON | ECHO | ECHOE | ISIG);
  //options.c_oflag     &= ~OPOST;
  //如果串口输入队列没有数据,程序将在read调用处阻塞
  options.c_cc[VMIN]  = 1;
  options.c_cc[VTIME] = 0; 

  if(tcsetattr(fd, TCSANOW, &options) == -1)    // 保存配置并立刻生效
    return -1; 

  //清空串口输入输出队列
  tcflush(fd, TCOFLUSH);
  tcflush(fd, TCIFLUSH); 

  return 0;
} 

/**************************************************************************************************
函数名称:Com_ChangeBaudrate()
函数功能:设定串口波特率
函数说明:无
入口参数:fd:串口设备文件描述符
           baud:比特率 300、600、1200、2400、4800、9600、19200、38400、57600、115200
出口参数:成功返回0,失败返回-1
调用实例:无
**************************************************************************************************/
int Com_ChangeBaudrate(int fd, unsigned int baud)
{
  struct termios options;
  struct termios old_options;
  unsigned int baudrate = B19200; 

  if (fd == -1)
    return -1; 

  if(tcgetattr(fd, &old_options) == -1)
    return -1; 

  if(tcgetattr(fd, &options) == -1)
    return -1; 

  switch (baud) {
  case 300:
    baudrate =  B300;
    break;
  case 600:
    baudrate =  B600;
    break;
  case 1200:
    baudrate =  B1200;
    break;
  case 2400:
    baudrate =  B2400;
    break;
  case 4800:
    baudrate =  B4800;
    break;
  case 9600:
    baudrate =  B9600;
    break;
  case 19200:
    baudrate =  B19200;
    break;
  case 38400:
    baudrate =  B38400;
    break;
  case 57600:
    baudrate =  B57600;
    break;
  case 115200:
    baudrate =  B115200;
    break;
  default:
    baudrate =  B19200;
    break;
  } 

  if(cfsetispeed(&options, baudrate) == -1)
    return -1; 

  if(cfsetospeed(&options, baudrate) == -1) {
    tcsetattr(fd, TCSANOW, &old_options);
    return -1;
  } 

  while(tcdrain(fd) == -1);//tcdrain(fd);保证输出队列中的所有数据都被传送 

  //清空串口输入输出队列
  tcflush(fd, TCOFLUSH);
  tcflush(fd, TCIFLUSH); 

  if(tcsetattr(fd, TCSANOW, &options) == -1) {
    tcsetattr(fd, TCSANOW, &old_options);
    return -1;
  } 

  return 0;
} 

/**************************************************************************************************
函数名称:Com_Read()
函数功能:接收数据
函数说明:无
入口参数:fd:串口设备文件描述符
           ReadBuffer:将数据写入ReadBuffer所指向的缓存区,并返回实际读到的字节数
           ReadSize:欲读取的字节数
出口参数:成功返回实际读到的字节数,失败返回-1
调用实例:无
**************************************************************************************************/
ssize_t Com_Read(int fd, unsigned char *ReadBuffer, ssize_t ReadSize)
{
  ssize_t rCount = 0;  //实际读到的字节数
  ssize_t dwBytesRead = 0;
  int InQueByteCount = 0; 

  if (fd < 0) {
    perror("file description is valid");
    return -1;
  } 

  if (ReadBuffer == NULL) {
    perror("read buf is NULL");
    return -1;
  } 

  if(ReadSize > COM_MAX_BUFFER)
    dwBytesRead = COM_MAX_BUFFER;
  else
    dwBytesRead = ReadSize; 

  memset(ReadBuffer, '\0', dwBytesRead); 

  if(Com_GetInQueByteCount(fd,&InQueByteCount) != -1) {
    printf("Uart Queue have %d bytes\n",InQueByteCount);
    dwBytesRead=UART_MIN(dwBytesRead,InQueByteCount);
  } 

  if(!dwBytesRead)
    return -1; 

  rCount = read(fd, ReadBuffer, dwBytesRead);
  if (rCount < 0) {
    perror("read error\n");
    return -1;
  } 

  return rCount;
} 

/**************************************************************************************************
函数名称:Com_Write()
函数功能:发送数据
函数说明:无
入口参数:fd:串口设备文件描述符
           WriteBuffer:将WriteBuffer所指向的缓冲区中的数据写入串口
           WriteSize:欲写入的字节数
出口参数:成功返回实际写入的字节数,失败返回-1
调用实例:无
**************************************************************************************************/
ssize_t Com_Write(int fd, unsigned char *WriteBuffer, ssize_t WriteSize)
{
  ssize_t wCount = 0;  //实际写入的字节数
  ssize_t dwBytesWrite=WriteSize; 

  if (fd < 0) {
    perror("file description is valid");
    return -1;
  } 

  if((dwBytesWrite > COM_MAX_BUFFER) || (!dwBytesWrite))
    return -1; 

  wCount = write(fd, WriteBuffer, dwBytesWrite);
  if (wCount < 0) {
    perror("write errot\n");
    return -1;
  } 

  while(tcdrain(fd) == -1); 

  return wCount;
} 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
#                       makefile
#
#    Develop Team     :    7fane Team
#    Team  Leader     :    He YiJun  (storysnail<at>gmail.com QQ:363559089)
#    Main Programmer  :    He YiJun
#    Program comments :    Ling Ying
#    License          :    7fane Team  License 1.0
#    Last Update      :    2013-03-25
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
all:hara-CtlPanel-linux
CC=gcc
CFLAGS = -Wall -ggdb
STD99 = -std=c99 

OBJFILES = main.o uart-linux.o 

hara-CtlPanel-linux:$(OBJFILES)
    $(CC) $(CFLAGS) $(STD99) $(OBJFILES) -o hara-CtlPanel-linux 

main.o:uart-linux.h
    $(CC) $(CFLAGS) -c main.c -o main.o 

uart.o:uart-linux.h
    $(CC) $(CFLAGS) -c uart-linux.c -o uart-linux.o 

clean:
    rm -f *.o *~ hara-CtlPanel-linux 

 

 

 

 

本文主要参考了
W.Richard stevens所著的《Advanced Programming in the UNIX Environment》
宋宝华编著的《linux设备驱动开发详解》
同时还参考了http://www.easysw.com/~mike/serial/serial.html的文章

 

 

 

 

            7fane Team 协议 1.0  中文版

    当您阅读、理解并愿意遵守以下条款时,您就拥有了获取、使用、复制、分发或通过通信
网络传播7fane Team作品的权利。

1. 7fane Team的作品可以在非商业用途下免费使用。

2. 如果软件提供了源代码,那么你可以更改源代码或软件接口以适应你的应用。

3. 在未获得7fane Team的授权之前,你不可以在商业用途下使用7fane Team的作品,
   也不可以将7fane Team的作品用于营利为目的的活动。关于获得许可,请发送Email
   到storysnail@gmail.com以获取更多信息。

4. 你不得租赁,再许可,出售,转让,抵押7fane Team的作品和服务。

5. 你不得删除或修改7fane Team作品的版权信息和相关的链接,例如网址信息或“关于窗口”
   中的所有信息,除非您已经获得7fane Team的书面授权。

6. 你不可以通过修改7fane Team的作品来获得衍生作品,更不可以重新分配这些衍生作品。

7. 如果您不能遵守本协议,您的许可将被终止,您必须停止使用并删除7fane Team作品及其
   副本,并且不可以再继续获取、使用、复制、分发或通过通信网络传播7fane Team作品。

8. 7fane Team拥有并保留修改本协议的权利和在本协议修改后不另行通知的权利。修改后
   的新协议将适用于新的许可用户。

9. 7fane Team作品是作为不提供任何明确的或隐含的赔偿或担保的形式提供的。

10.7fane Team对于使用其作品而生成的任何信息不承担任何责任,也不会对传播或使用
   这些信息承担责任。

11. 用户出于自愿而使用本7fane Team作品,您必须了解使用的风险,我们不承诺对用户提供
   任何形式的技术支持、使用担保,所以你必须承担全部风险。
关于 "7fane Team作品" 的定义:
    "7fane Team作品" 包括文本、图像、音频视频、软件等由7fane Team创作的一切。

    版权所有 (c)2001-2012,7fane Team 保留所有权利。


<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值