查看: 2883|回复: 2

盈鹏飞EVB335x试用之三:串口通信

[复制链接]
  • TA的每日心情
    开心
    2020-6-4 18:11
  • 签到天数: 75 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2018-5-15 20:46:40 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 zt1234 于 2018-6-6 10:02 编辑

    试用的目的是设计一个DTU,串口通信是基础。看看EVB335X的串口电路图如下: 串口原理图.png

    查看串口 ls -l /dev/tty*
    串口名称.png

    测试串口
    串口测试连线.png
    在EVB335X上执行#/usr/test/serial /dev/ttyO(大写欧)2 9600 8 0 1,则在测试串口上发送即可收到回送,测试成功。

    Linux对所有设备的访问是通过设备文件来进行的,访问串口只需打开系统的/dev下面的设备即可。串口属性定义在结构体structtermios中,详细参见https://blog.csdn.net/yemingzhu163/article/details/5873851,
    https://blog.csdn.net/u011192270/article/details/48174353
    例程如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>/*Unix 标准函数定义*/
    #include <fcntl.h>/*文件控制定义*/
    #include <string.h>
    #include <termio.h> /*PPSIX 终端控制定义*/
    #include <time.h>
    #define MAX_BUF_SIZE 1024
    char buf[MAX_BUF_SIZE+2];
    int read_data(int fd, void*buf, int len)
    {
       int count;
       int ret;
       ret = 0;
       count = 0;
       ret = read(fd,(char*)buf + count, len);
       if (ret < 1) {fprintf(stderr, "Read error%s\n", strerror(errno));}
       count += ret;
       len = len - ret;
       *((char*)buf + count) = 0;
       return count;
    }
    int write_data(int fd, void*buf, int len)
    {
       int count;
       int ret;
       ret = 0;
       count = 0;
       while (len > 0) {
           ret = write(fd,(char*)buf + count, len);
           if (ret < 1) {fprintf(stderr, "Write error%s\n", strerror(errno));break;}
           count += ret;
           len = len - ret;
       }
       return count;
    }

    int main(intargc, char *argv[])
    {
             int fd;
             int len,count,i;
             struct termio term_attr,oterm_attr;
             fd= open("/dev/ttyO2", O_RDWR);//ttyS0

                 if (ioctl(fd, TCGETA, &term_attr) < 0) {return -1;}// Get current setting
                 memcpy(&oterm_attr, &term_attr, sizeof(struct termio));//Backup old setting
                 term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL |ISTRIP);
                 term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);
                term_attr.c_lflag&= ~(ISIG | ECHO | ICANON | NOFLSH);
                 term_attr.c_cflag &= ~CBAUD;
                 term_attr.c_cflag |= CREAD |B9600;
                 term_attr.c_cflag &= ~(CSIZE);
                 term_attr.c_cflag |= CS8;
                 term_attr.c_cflag &= ~(PARENB);//nono
                 term_attr.c_cflag &= ~CSTOPB;//1
                 term_attr.c_cc[VMIN] = 1;
                 term_attr.c_cc[VTIME] = 0;
                 if (ioctl(fd, TCSETAW, &term_attr) < 0) {return -1;}
                 if (ioctl(fd, TCFLSH, 2) < 0) {return -1;}
             count= 0;
             while ( (len = read(fd, buf, MAX_BUF_SIZE)) > 0 )
             {
                       printf("Get data! %s\n",buf);//显示接收的数据
                       i= write_data(fd, buf, len);//回送接收的数据
                       if (i == 0) {fprintf(stderr, "Send data error!\n");break;}
             }
             if (ioctl(fd, TCSETAW, &oterm_attr) < 0){ return -1;}
             close(fd);
             return 0;

    }
    在ECLIPSE中编辑、编译、拷贝到EVB335X执行成功。
    串口成功.png

    调试单步执行时会停在read()函数上,等待读取数据,此为阻塞函数。
    串口阻塞.png

    read(intfdchar *pint nbyte),最后参数nbyte的意义是,如果缓存有大于nbyte个数的数据那么只返回nbyte个数据,剩余的数据下次再去读时才能读到。如果缓存中只有少于nbyte个数据,会等待options.c_cc[VTIME]的时间则返回,并且读取少于nbyte的实际数量的数据。这个等待就是阻塞,在单线程的程序里主线程被阻塞则整个程序被锁死。但非阻塞read函数在没有数据时会返回错误,一直查询返回值会降低效率。这个问题的解决方法一个是多线程,另一个方法就是select函数。
        这里先使用select函数先查询串口再读,并且延时到了就返回。int select(nfds, readfds, writefds,exceptfds, timeout) 超时返回0,失败返回-1,成功返回就绪的数目。其中ndfs要监视的句柄数,readfds监视的可读句柄集合,writefds监视的可写文件集合,exceptfds监视的异常文件集合,timeout监视的超时结束时间。该函数在readfdswritefds的文件可读或可写或有异常或超时就返回。struct timeval* timeout可以使select处于三种状态:第一若是NULL即不传入时间,就将select置于阻塞状态,一定等到某个文件发生为止;第二若将时间值设为00毫秒,就变成一个纯粹的非阻塞函数,不管是否有变化都立刻返回,文件无变化返回0,有变化返回一个正值;第三timeout的值大于0这就是等待的超时时间,即selecttimeout时间内阻塞,超时时间之内有事件就返回了,否则在超时后不管怎样一定返回。使用时涉及到的宏有全部清零的FD_ZERO(&rd),设置某一位的FD_SET(fd,&rd),测试某一位的FD_ISSET(fd,&rd)。测试代码如下

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <termio.h>
    #include <time.h>
    #define MAX_BUF_SIZE 1024
    char buf[MAX_BUF_SIZE+2];

    int main(intargc, char *argv[])
    {struct termio term_attr,oterm_attr;
                       int fd;
                 int nread,nwrite,err;
                 struct timeval tv;
                 char buff[]="Hello!\r\n";
                 fd= open("/dev/ttyO2", O_RDWR);

                 memcpy(&oterm_attr, &term_attr, sizeof(struct termio));//Backup old setting
                             term_attr.c_iflag &= ~(INLCR | IGNCR |ICRNL |ISTRIP);
                             term_attr.c_oflag &= ~(OPOST | ONLCR |OCRNL);
                            term_attr.c_lflag&= ~(ISIG | ECHO |ICANON | NOFLSH);
                             term_attr.c_cflag &= ~CBAUD;
                             term_attr.c_cflag |= CREAD |B9600;
                             term_attr.c_cflag &= ~(CSIZE);
                             term_attr.c_cflag |= CS8;
                             term_attr.c_cflag &= ~(PARENB);//nono
                             term_attr.c_cflag &= ~CSTOPB;//1
                             term_attr.c_cc[VMIN] = 1;
                             term_attr.c_cc[VTIME] = 0;
                             if (ioctl(fd, TCSETAW, &term_attr) < 0) {return -1;}
                             if (ioctl(fd, TCFLSH, 2) < 0) {return -1;}

                 nwrite=write(fd,buff,8);
                 printf("nwrite=%d,%s\n",nwrite,buff);
                 tv.tv_sec = 5;
                 tv.tv_usec = 0;
                 fd_set rd;
                FD_ZERO(&rd);//
                 FD_SET(fd,&rd);
                 while(FD_ISSET(fd,&rd))
                 {
                          err= select(fd+1,&rd,NULL,NULL,&tv);
                          if(err == 0){printf("select time out!\n");}  //超时
                          else if(err == -1){ printf("fail to select!\n");}  //失败
                          else {
                                    printf("data is available!\n");
                                    while((nread = read(fd,buff,8))>0){printf("nread = %d,%s\n",nread,buff);}
                             }//成功
                 }
                 close(fd);
                 return 0;
    }





    回复

    使用道具 举报

  • TA的每日心情
    开心
    2020-6-4 18:11
  • 签到天数: 75 天

    连续签到: 1 天

    [LV.6]常住居民II

     楼主| 发表于 2018-5-16 10:23:28 | 显示全部楼层
    测试结果,只有在VMIN=0时,read函数才在VTIME后返回。看来还是上多线程吧,开个接收线程,一直等待数据,收一个字节就发给主程序。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2020-6-4 18:11
  • 签到天数: 75 天

    连续签到: 1 天

    [LV.6]常住居民II

     楼主| 发表于 2018-5-21 15:54:38 | 显示全部楼层
    本帖最后由 zt1234 于 2018-5-21 15:57 编辑

    linux下的多线程int pthread_create(
    pthread_t *restrict tidp,// 线程ID
    const pthread_attr_t *restrict attr,//线程属性,默认为NULL
    void *(*start_rtn)(void *),//线程函数
    void *restrict arg//线程函数的参数
    ); //成功返回0;否则返回错误编码

    int pthread_join(pthread_t tid, void **status);其中tid为需要等待的线程,status为线程tid执行的函数的返回值

    使用ECLIPSE开发多线程程序要用到pthread库,但它不是标准的LINUX库,需要链接上libpthread.a。eclipse中添加libpthread.a库:Project->Properties->C/C++ Build->Settings->GCC C++Linker->Libraries,在Libraries(-l)中添加pthread,在Librariessearch path(-L)中添加crypto即可。一个串口多线程程序如下
    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<unistd.h>/*Unix 标准函数定义*/
    #include<fcntl.h>/*文件控制定义*/
    #include<string.h>
    #include<termio.h> /*PPSIX 终端控制定义*/
    typedefunsigned char uint8_t ;
    structtermio term_attr,oterm_attr;
    int portfd= -1;
    voidprint_frame(const char *desc,uint8_t *buf,int size);
    voidgetCompleteFrame(uint8_t *inBuf,int inCnt,uint8_t *outBuf,int *destCnt,int*readStatus);
    voidmonitor_serial_readable(void *arg);
    intmain(int argc,char *argv[])
    {
        int nret;
        pthread_t wtid;
        portfd = open("/dev/ttyO2",O_RDWR);
        if (ioctl(portfd, TCGETA, &term_attr)< 0) {return -1;}// Get current setting
                memcpy(&oterm_attr, &term_attr, sizeof(struct termio));//Backupold setting
                term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);
                term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);
                term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);
                term_attr.c_cflag &= ~CBAUD;
                term_attr.c_cflag |= CREAD |B9600;
                term_attr.c_cflag &= ~(CSIZE);
                term_attr.c_cflag |= CS8;
                term_attr.c_cflag &= ~(PARENB);//nono
                term_attr.c_cflag &= ~CSTOPB;//1
                term_attr.c_cc[VMIN] = 1;
                term_attr.c_cc[VTIME] = 0;
                if (ioctl(portfd, TCSETAW, &term_attr) < 0) {return -1;}
                if (ioctl(portfd, TCFLSH, 2) < 0) {return -1;}
        nret = pthread_create(&wtid,NULL,(void*) serial_readable,NULL);
        if(nret != 0)
        {
           printf("create threadfailed");
            exit(-1);
        }
        nret = pthread_join(wtid,NULL);
        if(nret != 0)
        {
          printf("join thread failed");
            exit(-1);
        }
        close(portfd);
        return 0;
    }
    void serial_readable(void*arg)
    {
        int rc,nread=0;
        fd_set rset;
        struct timeval tv;
        uint8_t buf[1024] = {0};
        uint8_t dest[1024]={0};
        int read_status = 0;
        int dest_cnt = 0;
        while(1)
        {
            FD_ZERO(&rset);
            FD_SET(portfd,&rset);
            tv.tv_sec = 5;
            tv.tv_usec = 0;
            rc =select(portfd+1,&rset,NULL,NULL,&tv);
            if(rc ==-1){printf("select errorin thread");continue;}
            if(rc == 0){printf("select timeoutin thread");continue;}
            else
            {
               nread = read(portfd,buf,sizeof(buf));
                if(nread == -1){
                         perror("read");
                    usleep(100*1000);
                    continue;
                }
                if(nread == 0)
                {
                    printf("nread==0\n");
                    usleep(100*1000);
                    continue;
                }
                printf("nread =%d\n",nread);
               getCompleteFrame(buf,nread,dest,&dest_cnt,&read_status);
            }
        }
    }
    voidgetCompleteFrame(uint8_t *inBuf,int inCnt,uint8_t *outBuf,int *destCnt,int*readStatus)
    {
        int i;
        for(i=0; i<inCnt; i++)
        {
            if(inBuf == 0x11 &&inBuf[i+2] == 0x00 && inBuf[i+3] == 0x00)//header
            {
                outBuf[(*destCnt)++] = inBuf;
                *readStatus = 1;
               continue;
            }
            if(*readStatus == 1)//body
            {
                outBuf[(*destCnt)++] = inBuf;
               //print_frame("body",dest,dest_cnt);
            }
            if(*destCnt == outBuf[1])//tail
            {
                print_frame("tail",outBuf,*destCnt);
                *readStatus = 0;
                *destCnt = 0;
                memset(outBuf,-1,sizeof(outBuf));
                memset(inBuf,0,sizeof(inBuf));
                continue;
            }
        }
    }
    voidprint_frame(const char *desc,uint8_t *buf,int size)
    {
        int i;
        printf("RED [%s][LEN=%d]COLOR_END",desc,size);
        for(i=0; i<size; i++){printf("BLUE[%.2x] COLOR_END",buf);}
        printf("\n");
    }

    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-16 22:37 , Processed in 0.134122 second(s), 20 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.