Chinaunix首页 | 论坛 | 博客
  • 博客访问: 313413
  • 博文数量: 169
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 12
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-10 12:00
文章分类

全部博文(169)

文章存档

2014年(38)

2013年(131)

我的朋友

分类: LINUX

2013-07-23 13:33:14

原文地址:linux 串口编程 作者:wangbaolin719

 1、串口概述
   
用户常见的数据通信的基本方式有两种:并行通信和串行通信。
   
串行通信是计算机常用的接口,如:RS-232-C接口。该标准规定采用一个DB25芯引脚连接器或DB9芯引脚连接器。芯片内部常具有UART控制器,其可工作于Interrupt(中断模式)DMA(直接内存访问)模式。
     UART
的操作主要包括以下几个部分:
数据发送;数据接收;产生中断;产生波特率;Loopback模式;红外模式;自动流控模式。
   
串口参数的配置主要包括:波特率、数据位、停止位、流控协议

    linux中的串口设备文件存放于/dev目录下,其中串口一,串口二对应设备名依次为“/dev/ttyS0”“/dev/ttyS1”。在linux下操作串口与操作文件相同。


2、串口详细配置
  
在使用串口之前必须设置相关配置,包括:波特率、数据位、校验位、停止位等。

串口设置由下面结构体实现:

Struct termios {
tcflag_t c_iflag; /*input flags */
tcflag_t c_oflag; /*output flags */
tcflag_t c_cflag; /*control flags */
tcflag_t c_lflag; /*local flags */
cc_t   c_cc[NCCS]; /*control characters */
};

    该结构中c_cflag最为重要,可设置波特率、数据位、校验位、停止位。在设置波特率时需在数字前加上‘B’,如B9600B19200。使用其需通过”“操作方式。

输入模式c_iflag成员控制端口接收端的字符输入处理。


串口控制函数:

Tcgetattr                      取属性(termios结构)
Tcsetattr                   
设置属性(termios结构)
cfgetispeed                 
得到输入速度
Cfgetospeed               
得到输出速度
Cfsetispeed                
设置输入速度
Cfsetospeed               
设置输出速度
Tcdrain                     
等待所有输出都被传输
tcflow                        
挂起传输或接收
tcflush                       
刷清未决输入和/或输出
Tcsendbreak             
BREAK字符
tcgetpgrp                   
得到前台进程组ID
tcsetpgrp                   
设置前台进程组ID

2.1 串口配置流程
1.
保存原先串口配置使用tcgetattr(fd,&oldtio)函数

            struct termios newtio,oldtio;
            tcgetattr( fd,&oldtio );

2. 激活选项有CLOCALCREAD,用于本地连接和接收使能。

             newtio.c_cflag | = CLOCAL | CREAD;

3. 设置波特率,使用cfsetispeedcfsetospeed函数。

             cfsetispeed(&newtio, B115200);
             cfsetospeed(&newtio, B115200);

4. 设置数据位,需使用掩码设置。

             newtio.c_cflag &= ~CSIZE;
             newtio.c_cflag |= CS8;

5. 设置奇偶校验位,使用c_cflagc_iflag
   
设置奇校验:

             newtio.c_cflag |= PARENB;
             newtio.c_cflag |= PARODD;
             newtio.c_iflag |= (INPCK | ISTRIP);

    设置偶校验:

              newtio.c_iflag |= (INPCK | ISTRIP);
              newtio.c_cflag |= PARENB;
              newtio.c_cflag &= ~PARODD;

6. 设置停止位,通过激活c_cflag中的CSTOPB实现。若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB

             newtio.c_cflag &= ~CSTOPB;

7. 设置最少字符和等待时间,对于接收字符和等待时间没有特别要求时,可设为0

              newtio.c_cc[VTIME] = 0;
              newtio.c_cc[VMIN] = 0;


8.
处理要写入的引用对象

    tcflush函数刷清输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。

             int tcflush(int filedes, int queue )

queue参数应当是下列三个常数之一:

? TCIFLUSH    刷清输入队列。
? TCOFLUSH  
刷清输出队列。
? TCIOFLUSH
刷清输入和输出队列。

9. 激活配置。在完成配置后,需激活配置使其生效。使用tcsettattr()函数。原型:

int tcgetattr(int filedes, struct termios * termptr);
int tcsetattr(int filedes, int opt, const struct termios * termptr);

tcsetattr的参数opt使我们可以指定在什么时候新的终端属性才起作用。opt可以指定为下列常数中的一个:

? TCSANOW    更改立即发生。
? TCSADRAIN
发送了所有输出后更改才发生。若更改输出参数则应使用此选择项。
? TCSAFLUSH
发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被删除。


3. 串口使用详解

     在配置完串口的相关属性后,就可对串口进行打开,读写操作了。其使用方式与文件操作一样,区别在于串口是一个终端设备。

3.1 打开串口

      fd = open( "/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
      Open
函数中除普通参数外,另有两个参数O_NOCTTYO_NDELAY
      O_NOCTTY:
通知linix系统,这个程序不会成为这个端口的控制终端。
      O_NDELAY:
通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
     
然后,恢复串口的状态为阻塞状态,用于等待串口数据的读入。用fcntl函数:

            fcntlfd, F_SETFL, 0;

      接着,测试打开的文件描述府是否引用一个终端设备,以进一步确认串口是否正确打开。

            isatty(STDIN_FILENO);


3.2 读写串口
      串口的读写与普通文件一样,使用read,write函数。

            read(fd,buff,8);
            write(fd,buff,8);

Example:seri.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

 // 关于串口的配置情况

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

{

//保存原先串口配置使用

     struct termios newtio,oldtio;

     if ( tcgetattr( fd,&oldtio) != 0)

     {

         perror("SetupSerial 1");

         return -1;

     }

// 将一段内存内容全部清零:将 newtio 所指的内存区域前 sizeof( newtio ) 个字节,全部设为0

//  <==> memset((void *) newtio,0, sizeof( newtio ))

     bzero( &newtio, sizeof( newtio ) );

// 激活选项有CLOCALCREAD,用于本地连接和接收使能

     newtio.c_cflag |= CLOCAL | CREAD;

//设置数据位,需使用掩码设置

     newtio.c_cflag &= ~CSIZE;

 

     switch( nBits )  // 数据位的判断

     {

         case 7:

              newtio.c_cflag |= CS7;

         break;

         case 8:

              newtio.c_cflag |= CS8;

         break;

     }

 

     switch( nEvent )  // 奇偶校验的判断

     {

         case 'O':     // 奇校验

              newtio.c_cflag |= PARENB;

              newtio.c_cflag |= PARODD;

              newtio.c_iflag |= (INPCK | ISTRIP);

         break;

        

         case 'E':     // 偶校验

              newtio.c_iflag |= (INPCK | ISTRIP);

              newtio.c_cflag |= PARENB;

              newtio.c_cflag &= ~PARODD;

         break;

         case 'N':     // 没有校验位

              newtio.c_cflag &= ~PARENB;

         break;

     }

 

     switch( nSpeed )   // 波特率的判断

     {

         case 2400:

              cfsetispeed(&newtio, B2400);

              cfsetospeed(&newtio, B2400);

         break;

         case 4800:

              cfsetispeed(&newtio, B4800);

              cfsetospeed(&newtio, B4800);

         break;

         case 9600:

              cfsetispeed(&newtio, B9600);

              cfsetospeed(&newtio, B9600);

         break;

         case 115200:       //////

              cfsetispeed(&newtio, B115200);

              cfsetospeed(&newtio, B115200);

         break;

         default:

              cfsetispeed(&newtio, B9600);

              cfsetospeed(&newtio, B9600);

         break;

     }

// 停止位的判断

     if( nStop == 1 )  

         newtio.c_cflag &= ~CSTOPB;

     else if ( nStop == 2 )

         newtio.c_cflag |= CSTOPB;

// 设置最少字符和等待时间,对于接收字符和等待时间没有特别要求时,可设为0

     newtio.c_cc[VTIME] = 0;

     newtio.c_cc[VMIN] = 0;

/* 处理要写入的引用对象

    tcflush函数刷清输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。queue参数应当是下列三个常数之一:

     ? TCIFLUSH    刷清输入队列。

     ? TCOFLUSH   刷清输出队列。

     ? TCIOFLUSH 刷清输入和输出队列。

*/

     tcflush(fd,TCIFLUSH);

/* 激活配置。在完成配置后,需激活配置使其生效。使用tcsettattr()函数。

tcsetattr的参数opt使我们可以指定在什么时候新的终端属性才起作用。opt可以指定为下列常数中的一个:

     ? TCSANOW    更改立即发生。

     ? TCSADRAIN 发送了所有输出后更改才发生。若更改输出参数则应使用此选择项。

     ? TCSAFLUSH 发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被删除。

*/

     if((tcsetattr(fd,TCSANOW,&newtio))!=0)

     {

         perror("com set error");

         return -1;

     }

     printf("set done!\n");

     return 0;

}

 // 打开驱动设备文件

int open_port(int fd,int comport)

{

     char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};

     long vdisable;

/*

O_NOCTTY: 通知linix系统,这个程序不会成为这个端口的控制终端。

O_NDELAY: 通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。

*/

     if (comport==1)

     {

         fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS0 .....\n");

     }

/////////////////////////////

     else if(comport==2)

     {

         fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS1 .....\n");

     }

/////////////////////////////

     else if (comport==3)

     {

         fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);

         if (-1 == fd)

         {

              perror("Can't Open Serial Port");

              return(-1);

         }

         else

              printf("open ttyS2 .....\n");

     }

// 恢复串口的状态为阻塞状态,用于等待串口数据的读入。用fcntl函数:

     if(fcntl(fd, F_SETFL, 0)<0)

         printf("fcntl failed!\n");

     else

         printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));

// 测试打开的文件描述府是否引用一个终端设备,以进一步确认串口是否正确打开。

     if(isatty(STDIN_FILENO)==0)

         printf("standard input is not a terminal device\n");

     else

         printf("isatty success!\n");

     printf("fd-open=%d\n",fd);

     return fd;

}

 

int main(void)

{

     int fd;

     int nread,i;

     char buff[]="Hello\n";

 

     if((fd=open_port(fd,1))<0)

     {

         perror("open_port error");

         return;

     }

     if((i=set_opt(fd,115200,8,'N',1))<0)

     {

         perror("set_opt error");

         return;

     }

     printf("fd=%d\n",fd);

// fd=3;

     nread=read(fd,buff,8);

     printf("nread=%d,%s\n",nread,buff);

     close(fd);

}

阅读(938) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~