Linux下的串口编程,在嵌入式开发中占据着重要的地位,因为很多的嵌入式设备都是通过串口交换数据的。在没有操作系统的我们可以使用UART的中断来出来数据的接受和发送,而在Linux操作系统下,我们也可以使用软中断的方式来处理数据的接受和发送,这里主要使用的是信号SIGIO,也就是异步I/O。这里也可以使用select实现异步形式的通知。  这里可以参考《UNIX 环境高级编程》中的第14章 高级I/O和第18章的I/O终端,这两章描述了串口的编程和异步I/O方面的内容。还有一本书《linux serial programming how-to》,《Serial Programming Guide for POSIX Operating Systems》。这都是串口编程的必读和经典书籍。

串口参数的设置一般包括波特率、起始位数量、数据位、停止位和流控协议。在接收端和发送端要配置成一样的参数设置。在Linux中,所有的设备文件一般都位于“/dev”下,其中串口一、串口二对应的设备名依次为“/dev/ttyS0”、"/dev/ttyS1"。这可以通过查看"/dev"下的文件加以确认。我的串口通信是开发板ARM9--mini2440发送数据,PC机通过串口接受数据。我的串口的参数设置为 115200,8,‘N’,1。也就是波特率是115200,8位数据位,无奇偶校验位,1位停止位。因为是用的开发板发送数据,所以要用到在minicom中运行发送的程序,不过在发送程序运行后,要立即关闭minicom,否则,接受程序不能接受到数据。这个是我使用中断时出现的问题,当我使用select是没有此问题,现在还不知道具体的原因是什么。

串口编程中有一个最重要的结构体:

struct termios
{
tcflag_t c_iflag; /* 输入选项标志 */
tcflag_t c_oflag; /* 输出选项标志 */
tcflag_t c_cflag; /* 控制选项标志 */
tcflag_t c_lflag; /* 本地选项标志 */

unsigned char c_line /*线控制*/
cc_t c_cc[NCCS]; /* 控制特性 */
};

这个结构中最重要的是c_cflag。通过对它的赋值,用户可以设置波特率、字符大小、数据位、停止位、奇偶校验位和硬件流控等。其中的参数在网上和很多的书籍上都介绍的很详细了,这里主要介绍一下我在其中遇到的问题和解决的办法,以供学习。其中的c_line,在POSIX中的Linux中没有用到。

在c_lflag中有这么一个参数ICANON,如若设置,则按规范模式工作,这使下列字符起作用:EOF、EOL EOL2、 ERASE、 KILL、 REPRINT 、STATUS、WERASE。输入字符被装配成行。如果不以规范模式工作,则读请求直接从输入队列取字符。在至少接收到MIN个字节或已超过TIME值之前,read将不返回。

在规范模式很容易:系统每次返回一行。但在非规范模式下,系统怎样才能知道在什么时候将数据返回给我们呢?如果它一次返回一个字节,那么系统开销就很大。解决方法是:当已读了指定量的数据后,或者已经过了给定的时间后,即通知系统返回。这种技术使用了termios结构中c_cc数组的两个变量:MIN和TIME。C_cc数据中的这两个元素的下标名为VMIN和VTIME。MIN说明一个read返回前的最小字节数。TIME说明等待数据到达的分秒数。

需要包括的头文件是:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include "Set_Uart.c"

其中的“Set_Uart.c”是我设置串口的模式和打开串口的文件。模式的设置就是按上面说的进行设置。

下面是发送数据,写串口的程序,

int main(void)
{
  int fd;
  int nwrite,i;
  char buff[] = "Hello world!\n";
  fd = 0;
  /*打开串口*/
  if((fd = open_port(fd,0)) < 0) //在我的Set_Uart.c文件中定义。

{
    perror("open_port error!\n");
    return (-1);
  }
  /*设置串口*/
  if((i = set_opt(fd,115200,8,'N',1)) < 0) //在Set_Uart.c文件中定义

{
    perror("set_opt error!\n");
    return (-1);
  }
  printf("fd =%d\n",fd);
  /*向串口写入字符串*/
sleep(10);  
  nwrite = write(fd,buff,strlen(buff));
sleep(10);
  printf("nwrite = %d\n",nwrite);
  close(fd);
  return (1);
}

把该文件进行交叉编译后下载到开发板,进行运行./Write_Uart。

下面是中断读取数据的main函数。Read_Uart_IRQ.c
当使用中断方式的读取数据时,要先运行开发板上的Write_Uart.c文件,然后,立即关闭,再在PC上运行读取数据的
Read_Uart_IRQ.c文件。所以,在Write_Uart.c中,在使用write()函数向UART写数据之间加入一小段的延时。这样便于关
闭minicom,如果在两台PC上进行测试的话,应该不存在此问题。

int main(void)
{
  int fd,res,i;
  struct sigaction saio; /*definition of signal axtion */
  char buf[255];
  fd_set rd;
  fd = 0;
  /*打开串口*/
  if((fd = open_port(fd,1))<0)
  {
    perror("open_port error!\n");
    return (-1);
  }
  /* install the signal handle before making the device asynchronous*/
  saio.sa_handler = signal_handler_IO;
  sigemptyset(&saio.sa_mask);
  //saio.sa_mask = 0; 必须用sigemptyset函数初始话act结构的sa_mask成员

saio.sa_flags = 0;
  saio.sa_restorer = NULL;
  sigaction(SIGIO,&saio,NULL);

/* allow the process to recevie SIGIO*/
  fcntl(fd,F_SETOWN,getpid());
  /* Make the file descriptor asynchronous*/
  fcntl(fd,F_SETFL,FASYNC);
  
  /*设置串口*/
  if((i= set_opt(fd,115200,8,'N',1))<0)
  {
    perror("set_opt error!\n");
    return (-1);
  }
  /* loop while waiting for input,normally we would do something useful here*/
  while(STOP == FALSE)
  {
    usleep(100000);
    /* after receving SIGIO ,wait_flag = FALSE,input is availabe and can be read*/
    if(wait_flag == FALSE)
    {
          memset(buf,0,255);
      res = read(fd,buf,255);
       printf("nread=%d,%s\n",res,buf);
      if(res == 1)
        STOP = TRUE; /*stop loop if only a CR was input */
       wait_flag = TRUE; /*wait for new input*/
    }
  }
  close(fd);
  return 0; 
}
/******************************************
 信号处理函数,设备wait_flag=FASLE
 ******************************************************/
void signal_handler_IO(int status)
{
   printf("received SIGIO signale.\n");
  wait_flag = FALSE; 
}

下面是select方式的读取数据的main函数。Read_Uart.c

int main(void)
{
  int fd;
  int nread,nwrite,i;
  char buff[8];
  fd_set rd;
  fd = 0;
  /*打开串口*/
  if((fd = open_port(fd,1)) < 0)
  {
    perror("open_port error!\n");
    return ;
  }
  /*设置串口*/
  if((i= set_opt(fd,115200,8,'N',1)) < 0)
  {
    perror("set_opt error!\n");
    return (-1);
  }
  /*利用select函数来实现多个串口的读写*/
while(1)
{
  FD_ZERO(&rd);
  FD_SET(fd,&rd);
  while(FD_ISSET(fd,&rd))
  {
    if(select(fd+1,&rd,NULL,NULL,NULL) < 0)
      perror("select error!\n");
    else
    {
      while((nread = read(fd,buff,8))>0)
      {
        printf("nread = %d,%s\n",nread,buff);
     printf("nread = %d,%s\n",nread,buff);
      }
    }
  }
}
close(fd);
    return ;
}

下面是运行的结果,PC机收到的开发板发送过来的数据。

./Read_Uart_IRQ
fcntl = 0
isatty success !
fd-open=3
set
received SIGIO signale.
nread=13,Hello

其中串口中的一些重要的设备如下;

/*设置等待时间它最小接收字符*/
  newtio.c_cc[VTIME] = 1;
  newtio.c_cc[VMIN] = 0;
  newtio.c_lflag &= ~( ECHO | ECHOE | ISIG);
  newtio.c_lflag |=ICANON; //关闭ICANON标志就使终端处于非规范模式 现在处于打开 处于规范模式下
  newtio.c_oflag &= ~OPOST; //执行输出处理 现在就关闭状态
  newtio.c_iflag |= (IGNPAR | ICRNL); //忽略奇偶校验错误 将CR 映射成NL

http://blog.chinaunix.net/uid-20788636-id-1841319.html

Linux串口编程(中断方式和select方式)的更多相关文章

  1. linux串口编程参数配置详解(转)

    1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义#include <stdlib.h>        //标准函数 ...

  2. linux串口编程参数配置详解

    1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义 #include <stdlib.h>        //标准函 ...

  3. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

  4. Linux串口编程详解(转)

    串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...

  5. linux串口编程总结

    串口本身.标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口以前被广泛用于连接计算机和终端设备和各种外部设备.尽管以太网接口和USB接口也是以一个串行流进行数据传送的.可是串口连接 ...

  6. Linux串口编程进阶

    在<Linux串口编程>编程一文中介绍了串口应用中常用的基本操作,如:串口打开关闭.串口设置.数据收发等.本篇文章主要基于常规串口操作进行了扩充,主要介绍如下操作: Linux系统使用非标 ...

  7. linux串口编程

    按照对linux系统的理解,串口编程的顺序无非就是open,read,write,close,而串口有波特率.数据位等重要参数需要设置,因此还应该用到设置函数,那么接下来就带着这几个问题去学习linu ...

  8. Linux串口编程(转载)

    在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串口编程时 ...

  9. linux串口编程设置(转载)

    (转载)在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串 ...

随机推荐

  1. 带宽的单位为什么是Hz而不是bps?

    如果从电子电路角度出发,带宽(Bandwidth)本意指的是电子电路中存在一个固有通频带,这个概念或许比较抽象,我们有必要作进一步解释.大家都知道,各类复杂的电子电路无一例外都存在电感.电容或相当功能 ...

  2. response.sendRedirect 的功能是地址重定向(页面跳转)

    response.sendRedirect 的功能是地址重定向(页面跳转) 1.response.sendredirect(url); 新的页面并不能处理旧页面的pagecontext(request ...

  3. DBCP与C3P0数据库连接池

    数据库连接池是做什么的? 学过计算机网络的都知道,在一个内部局域网中.大部分用的都是私有地址,要想和外部 打交道,必须要有相应的合法外部地址相相应.然而内部用户数量巨大.一台机子一个外部IP 是不现实 ...

  4. tcp/iP协议族——IP工作原理及实例具体解释(下)

     IP协议具体解释 上一篇文章文章主要介绍了IP服务的特点,IPv4头部结构IP分片.并用tcpdump抓取数据包,来观察IP数据报传送过程中IP的格式,以及分片的过程.本文主要介绍IP路由,IP ...

  5. python_selenium之xpath的使用

    python_selenium之xpath的使用 一.xpath介绍 Xpath:XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言.XPath基于X ...

  6. 在程序内部使用winGraphviz进行图形自动布局

    winGraphviz支持內部圖形形狀進行佈局圖輸出.當然,在我們程序內部並不需要這樣的一個圖,因為我們的圖可能需要其它的繪製元素,而且我們還會在圖形上進行拖動.放大.縮小等功能,一張簡單的佈局圖是不 ...

  7. PHP 可以获取客户端哪些访问信息

    php是一种弱类型的程序语言,但是最web的 在程序语言中有系统全局函数: $_SERVER <?php echo "".$_SERVER['PHP_SELF'];#当前正在 ...

  8. <转> 堆和栈的区别

    一.预备知识—程序的内存分配    一个由C/C++编译的程序占用的内存分为以下几个部分    1.栈区(stack)—   由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数 ...

  9. EasyDSS流媒体服务器软件支持HTTPS-启用https服务申请免费证书

    EasyDSS流媒体服务器软件,提供一站式的转码.点播.直播.时移回放服务,极大地简化了开发和集成的工作. 其中,点播功能主要包含:上传.转码.分发.直播功能,主要包含:直播.录像, 直播支持RTMP ...

  10. 前端 为什么我选择用框架而不是Jquery

    对于很多习惯用Jquery的前端甚至后端,都很不解,为什么不用Jquery而是框架.觉得框架学起来麻烦,成本高,今天我以我浅薄的知识来总结一下为什么前台开发选择用框架: 前台开发,主要的性能是卡在回流 ...