zoukankan      html  css  js  c++  java
  • 【转】Linux C下非特定波特率的配置和使用

    https://blog.csdn.net/jinhongdu/article/details/43413071

     对于非标准的任意波特率需要用ioctl(fd, TIOCGSERIAL,  p)和ioctl(fd, TIOCSSERIAL, p)的配合,ioctl的最后一个参数是struct serial_struct  *类型,在linux/serial.h中定义。
     
     其中baud_base是基准晶振频率/16,通常是115200,你需要设的是custom_divisor这个值,最终的波特率为baud_base/custom_divisor,比如你需要28800,因为115200/4=28800,所以要设置custom_divisor=4,。
     具体过程为,先设置波特率设为38400(tcsetattr),然后用TIOCGSERIAL得到当前的设置,将flags设置ASYNC_SPD_CUST位,设置custom_divisor,最后用TIOCSSERIAL设置。
     
     使用setserial其实就是利用上述方法,来设置baud_base, custom_divisor等, 其内部实现就是使用ioctl来进行设置。
     
     网上的东西真的是参差不齐,希望能呈现完善的正确的Blog给大家。
     由于是测试代码,只是保证可以运行。另外推荐一个串口调试助手AccessPort,可以提供28800的串口比特率作为测试。

    #include <termios.h>
    #include <sys/ioctl.h>
    #include <stdio.h>      /*标准输入输出定义*/
    #include <stdlib.h>     /*标准函数库定义*/
    #include <unistd.h>     /*Unix标准函数定义*/
    #include <sys/types.h>  /**/
    #include <sys/stat.h>   /**/
    #include <fcntl.h>      /*文件控制定义*/
    #include <termios.h>    /*PPSIX终端控制定义*/
    #include <errno.h>      /*错误号定义*/
    #include <linux/serial.h>
    #define TRUE 1
    #define FALSE 0
    /*
    *功能:用于测试非标准波特率串口。
    *此代码仅限于运行在X86架构的环境下,其他架构并未测试。在arm下未测试
    *请在root下编译此代码
    *如有问题,联系我:靳小都 hellojinhongdu#126.com
    */
    struct serial_t {
        int     fd;
        char    *device;/*/dev/ttyS0,...*/
        int     baud;
        int     databit;/*5,6,7,8*/
        char    parity;/*O,E,N*/
        int    stopbit;/*1,2*/
        int    startbit;/*1*/
        struct termios    options;
    };
    int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
            B38400, B19200, B9600, B4800, B2400, B1200, B300, };
    int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,
            38400,  19200,  9600, 4800, 2400, 1200,  300, };
    void set_speed(int fd, int speed)
    {
      int   i;
      int   status;
      struct termios   Opt;
      tcgetattr(fd, &Opt);
      for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
       {
        if  (speed == name_arr[i])
        {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&Opt, speed_arr[i]);
            cfsetospeed(&Opt, speed_arr[i]);
            status = tcsetattr(fd, TCSANOW, &Opt);
            if  (status != 0)
                perror("tcsetattr fd1");
            return;
            }
       tcflush(fd,TCIOFLUSH);
       }
    }
    //设置为特诉波特率,比如28800
    int serial_set_speci_baud(struct serial_t *tty,int baud)
    {
        struct serial_struct ss,ss_set;
        tcgetattr(tty->fd,&tty->options);
        cfsetispeed(&tty->options,B38400);
        cfsetospeed(&tty->options,B38400);
        tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
        tcsetattr(tty->fd,TCSANOW,&tty->options);
        if((ioctl(tty->fd,TIOCGSERIAL,&ss))<0){
            printf("BAUD: error to get the serial_struct info:%s ",strerror(errno));
            return -1;
        }
        ss.flags = ASYNC_SPD_CUST;
        ss.custom_divisor = ss.baud_base / baud;
        printf("ss.custom_divisor = %d ",ss.custom_divisor);
        if((ioctl(tty->fd,TIOCSSERIAL,&ss))<0){
            printf("BAUD: error to set serial_struct:%s ",strerror(errno));
            //return -2;
        }
        ioctl(tty->fd,TIOCGSERIAL,&ss_set);
        printf("BAUD: success set baud to %d,custom_divisor=%d,baud_base=%d ",
                baud,ss_set.custom_divisor,ss_set.baud_base);
        return 0;
    }
    /*get serial's current attribute*/
    static int serial_get_attr(struct serial_t *tty)
    {
        if(tcgetattr(tty->fd,&tty->options) != 0){
            printf("SERIAL: can't get serial's attribute ");
            return -1;
    }
        return 0;
    }
    /*update serial's attrbute*/
    static int serial_attr_update(struct serial_t *tty)
    {
        tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
        if((tcsetattr(tty->fd,TCSANOW,&tty->options)) < 0){
            return -1;
    }
        return 0;
    }
    static int serial_init_databit(struct serial_t *tty)
    {
        if(serial_get_attr(tty)<0)
            return -1;
        tty->options.c_cflag &= ~CSIZE;
        switch(tty->databit){
            case 5: tty->options.c_cflag |= CS5;break;
            case 6: tty->options.c_cflag |= CS6;break;
            case 7: tty->options.c_cflag |= CS7;break;
            case 8: tty->options.c_cflag |= CS8;break;
            default:
                printf("SERIAL: unsupported databit %d ",tty->databit);
                return -2;    
    }
        if(serial_attr_update(tty) < 0)
            return -3;
        printf("SERIAL: set databit to %d ",tty->databit);
        return 0;
    }
    static int serial_init_parity(struct serial_t *tty)
    {
        if(serial_get_attr(tty)<0)
            return -1;
        /*ignore framing and parity error*/
        tty->options.c_iflag = IGNPAR;
        switch (tty->parity){
            case 'n':
            case 'N':
                /* Clear parity enable */
                tty->options.c_cflag &= ~PARENB;
                /* Enable parity checking */
                tty->options.c_iflag &= ~INPCK;
                break;
            case 'o':
            case 'O':
                /* 设置为奇校检*/
                tty->options.c_cflag |= (PARODD|PARENB);
                /* Disnable parity checking */
                tty->options.c_iflag |= (INPCK|ISTRIP);
                break;
            case 'e':
            case 'E':
                /* Enable parity */
                tty->options.c_cflag |= PARENB;
                /* 转换为偶效验*/
                tty->options.c_cflag &= ~PARODD;
                /* Disnable parity checking */
                tty->options.c_iflag |= (INPCK|ISTRIP);  
                break;
            default:
                printf("SERIAL: unsupported parity %c ",tty->parity);
                return -2;
    }
        if(serial_attr_update(tty) < 0)
            return -3;
        printf("SERIAL: set parity to %c ",tty->parity);
        return 0;
    }
    static int serial_init_stopbit(struct serial_t *tty)
    {
        if(serial_get_attr(tty)<0)
            return -1;
        switch(tty->stopbit){
            case 1:
                tty->options.c_cflag &= ~CSTOPB;break;
            case 2:
                tty->options.c_cflag |= CSTOPB;break;
            default:
                printf("SERIAL: unsupported stopbit %d ",tty->stopbit);  
                return -2;    
    }
        if(serial_attr_update(tty) < 0)
            return -3;
        printf("SERIAL: set stopbit to %d ",tty->stopbit);
        return 0;
    }
    /**
    *@brief   设置串口数据位,停止位和效验位
    *@param  fd     类型  int  打开的串口文件句柄*
    *@param  databits 类型  int 数据位   取值 为 7 或者8*
    *@param  stopbits 类型  int 停止位   取值为 1 或者2*
    *@param  parity  类型  int  效验类型 取值为N,E,O,,S
    */
    int set_Parity(int fd,int databits,int stopbits,int parity)
    {
        struct termios options;
     if  ( tcgetattr( fd,&options)  !=  0)
      {
        perror("SetupSerial 1");
        return(FALSE);
      }
      options.c_cflag &= ~CSIZE;
      switch (databits) /*设置数据位数*/
      {
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            fprintf(stderr,"Unsupported data size ");
            return (FALSE);
        }
      switch (parity)
        {
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;   /* Clear parity enable */
            options.c_iflag &= ~INPCK;     /* Enable parity checking */
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;     /* Enable parity */
            options.c_cflag &= ~PARODD;   /* 转换为偶效验*/
            options.c_iflag |= INPCK;       /* Disnable parity checking */
            break;
        case 'S':
        case 's':  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported parity ");
            return (FALSE);
            }
      /* 设置停止位*/
      switch (stopbits)
        {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported stop bits ");
            return (FALSE);
        }
      /* Set input parity option */
      if (parity != 'n')
            options.c_iflag |= INPCK;
        options.c_cc[VTIME] = 150; // 15 seconds
        options.c_cc[VMIN] = 0;
      tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
      if (tcsetattr(fd,TCSANOW,&options) != 0)
        {
            perror("SetupSerial 3");
            return (FALSE);
        }
      return (TRUE);
     }
    //用法:只要指定serial_t的baud就可以了
    static struct serial_t __seri_conf[] = {
        [0] = {//connect with b board, ttyS0
            .device = "/dev/ttyS1",
            .baud = 28800,
            .databit = 8,
            .parity = 'N',
            .stopbit = 1,
        },
    };
    /**
    *@breif     main()
    */
    int main(int argc, char **argv) {
        int fd;
        int nread;
        char buff[512];
        fd = open(__seri_conf->device, O_RDWR | O_NOCTTY | O_NONBLOCK);
        __seri_conf->fd = fd;
        serial_set_speci_baud(__seri_conf, __seri_conf->baud);
        /*
        if (set_Parity(__seri_conf->fd, 8, 1, 'N') == FALSE)
            {
                printf("Set Parity Error ");
            }
        */
        if(serial_init_databit(__seri_conf)<0)
            printf("serial_init_databit error ");
        if(serial_init_parity(__seri_conf)<0)
            printf("serial_init_parity error ");
        if(serial_init_stopbit(__seri_conf)<0)
            printf("serial_init_stopbit error ");
        //struct termios opt;
        tcgetattr(__seri_conf->fd,&__seri_conf->options);
        __seri_conf->options.c_iflag &=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
        __seri_conf->options.c_lflag &=~(ICANON|ECHO|ECHOE|ECHONL|ISIG|IEXTEN);
        __seri_conf->options.c_oflag &=~(OPOST);
        if(tcsetattr(__seri_conf->fd,TCSANOW,&__seri_conf->options)!=0)
            printf("error");
        while (1) {
            char ct[10] = "hello";
            write(__seri_conf->fd, ct, 10);
            puts("hello world 28800 test 8888! ");
            sleep(1);
            nread = read(__seri_conf->fd,buff,256);
            if(nread>0)
            {
                printf("recv :%d ***%s ",nread,buff);           
            }
        }
        close(fd);
    }
  • 相关阅读:
    Google新闻昨晚发生全球服务中断 波及国内 狼人:
    德国"反黑客"法出炉:拥有黑客工具是非法的 狼人:
    百付宝携手瑞星 打造零风险支付平台 狼人:
    四月新增电脑病毒180万 2千万台次电脑遭攻击 狼人:
    PDF文件和Word文档面临更多网络安全威胁 狼人:
    麻省理工学生令计算机系统升级不需重启 狼人:
    nullnullIOS控件AlertView的使用
    设置源ARM中断处理_S3C2440
    软件授权码Python之道Python连接MYSQL数据库和发送邮件
    目标文件符号《深入理解计算机系统》笔记(三)链接知识【附图】
  • 原文地址:https://www.cnblogs.com/schips/p/10265178.html
Copyright © 2011-2022 走看看