zoukankan      html  css  js  c++  java
  • Linux串口编程(中断方式和select方式)

      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机通过串口接受数据。我的串口的参数设置为 1152008,‘N’,1。也就是波特率是1152008位数据位,无奇偶校验位,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数组的两个变量:MINTIMEC_cc数据中的这两个元素的下标名为VMINVTIMEMIN说明一个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! ";
      fd = 0;
      /*打开串口*/
      if((fd = open_port(fd,0)) < 0) //在我的Set_Uart.c文件中定义。

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

      {
        perror("set_opt error! ");
        return (-1);
      }
      printf("fd =%d ",fd);
      /*向串口写入字符串*/
    sleep(10);  
      nwrite = write(fd,buff,strlen(buff));
    sleep(10);
      printf("nwrite = %d ",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! ");
        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! ");
        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 ",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. ");
      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! ");
        return ;
      }
      /*设置串口*/
      if((i= set_opt(fd,115200,8,'N',1)) < 0)
      {
        perror("set_opt error! ");
        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! ");
        else
        {
          while((nread = read(fd,buff,8))>0)
          {
            printf("nread = %d,%s ",nread,buff);
         printf("nread = %d,%s ",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

  • 相关阅读:
    记录阿里云服务器mysql被黑
    微服务SpringCloud容器化案例
    优雅的启动、停止、重启你的SpringBoot项目
    java模式:建造者模式
    java集合 线程安全
    挖坑:hive集成kerberos
    挖坑:handoop2.6 开启kerberos(全流程学习记录)
    Specified version of key is not available (44)
    Mysql数据按天分区,定期删除
    maven项目打包额外lib目录
  • 原文地址:https://www.cnblogs.com/subo_peng/p/5291813.html
Copyright © 2011-2022 走看看