zoukankan      html  css  js  c++  java
  • 实现串口转以太、以太转串口

    一、源码准备

    1、 在barrier_breaker/package/utils文件夹下新建ttl_client文件

    2、 barrier_breaker/package/utils/ttl_client文件夹下的Makefile文件如下

    #
    # Copyright (C) 2009 OpenWrt.org
    #
    # This is free software, licensed under the GNU General Public License v2.
    # See /LICENSE for more information.
    #
    
    include $(TOPDIR)/rules.mk
    include $(INCLUDE_DIR)/kernel.mk
    
    PKG_NAME:=ttl_client
    PKG_RELEASE:=1
    PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
    
    include $(INCLUDE_DIR)/package.mk
    
    define Package/ttl_client
      SECTION:=utils
      CATEGORY:=Utilities
      TITLE:=serial to tcp 
      DEPENDS:=+libuci +libpthread
    endef
    
    define Package/ttl_client/description
      A client of tcp to serial or serial to tcp
    endef
    
    define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) ./src/* $(PKG_BUILD_DIR)/
    endef
    
    define Build/Configure
    endef
    
    define Build/Compile
        $(MAKE) -C $(PKG_BUILD_DIR) 
            CC="$(TARGET_CC)" 
            CFLAGS="$(TARGET_CFLAGS) -Wall -I$(LINUX_DIR)/user_headers/include" 
            LDFLAGS="$(TARGET_LDFLAGS)"
    endef
    
    define Package/ttl_client/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_DIR) $(1)/etc/config
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/ttl_client $(1)/usr/sbin/
    endef
    
    $(eval $(call BuildPackage,ttl_client))

    3、 新建src文件夹,barrier_breaker/package/utils/ttl_client/src的Makefile文件如下

    CC = gcc
    CFLAGS = -Wall
    OBJS = ttl_client.o
    
    all: ttl_client
    
    %.o: %.c
        $(CC) $(CFLAGS) -c -o $@ $< 
    
    ttl_client: $(OBJS)
        $(CC) -o $@ $(OBJS) -luci -lpthread
    
    clean:
        rm -f rbcfg *.o

    4、barrier_breaker/package/utils/ttl_client/src的ttl_client.c文件如下

    #include    <stdio.h>
    #include    <stdlib.h>
    #include    <string.h>
    #include    <errno.h>
    #include    <sys/types.h>
    #include    <sys/socket.h>
    #include    <netinet/in.h>
    #include     <unistd.h>  
    #include     <fcntl.h> 
    #include     <termios.h>
    #include     <errno.h>
    #include     <strings.h>
    #include     <time.h> 
    #include     <arpa/inet.h>
    #include     <pthread.h>
    
    #define SER_MAXLINE 128
    #define SOCK_MAXLINE 136//为了应对缓存一出,多分配一个字节
    struct argument  
    {  
        int fd;
        int sockfd;
    };  
    //为了保证用户输入的波特率是个正确的值,所以需要这两个数组验证,对于设置波特率时候,前面要加个B   
    int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,  
        B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, };  
      
    int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,  
        115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, };  
      
    /*----------------------------------------------------------------------------- 
      函数名:      set_speed 
      参数:        int fd ,int speed 
      返回值:      void 
      描述:        设置fd表述符的串口波特率 
     *-----------------------------------------------------------------------------*/  
    void set_speed(int fd ,int speed)  
    {  
        struct termios opt;  
        int i;  
        int status;  
      
        tcgetattr(fd,&opt);  
        for(i = 0;i < sizeof(speed_arr)/sizeof(int);i++)  
        {  
            if(speed == name_arr[i])                        //找到标准的波特率与用户一致   
            {  
                tcflush(fd,TCIOFLUSH);                      //清除IO输入和输出缓存   
                cfsetispeed(&opt,speed_arr[i]);         //设置串口输入波特率   
                cfsetospeed(&opt,speed_arr[i]);         //设置串口输出波特率   
      
                status = tcsetattr(fd,TCSANOW,&opt);    //将属性设置到opt的数据结构中,并且立即生效   
                if(status != 0)  
                    perror("tcsetattr fd:");                //设置失败   
                return ;  
            }  
            tcflush(fd,TCIOFLUSH);                          //每次清除IO缓存   
        }  
    }  
    /*----------------------------------------------------------------------------- 
      函数名:      set_parity 
      参数:        int fd 
      返回值:      int 
      描述:        设置fd表述符的奇偶校验 
     *-----------------------------------------------------------------------------*/  
    int set_parity(int fd)  
    {  
        struct termios opt;  
      
        if(tcgetattr(fd,&opt) != 0)                 //或许原先的配置信息   
        {  
            perror("Get opt in parity error:");  
            return -1;  
        }  
      
        /*通过设置opt数据结构,来配置相关功能,以下为八个数据位,不使能奇偶校验*/  
        opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP  
                    | INLCR | IGNCR | ICRNL | IXON);  
        opt.c_oflag &= ~OPOST;  
        opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);  
        opt.c_cflag &= ~(CSIZE | PARENB);  
        opt.c_cflag |= CS8;  
      
        tcflush(fd,TCIFLUSH);                           //清空输入缓存   
      
        if(tcsetattr(fd,TCSANOW,&opt) != 0)  
        {  
            perror("set attr parity error:");  
            return -1;  
        }  
      
        return 0;  
    }  
    /*----------------------------------------------------------------------------- 
      函数名:      serial_init 
      参数:        char *dev_path,int speed,int is_block 
      返回值:      初始化成功返回打开的文件描述符 
      描述:        串口初始化,根据串口文件路径名,串口的速度,和串口是否阻塞,block为1表示阻塞 
     *-----------------------------------------------------------------------------*/  
    int serial_init(char *dev_path,int speed,int is_block)  
    {  
        int fd;  
        int flag;  
      
        flag = 0;  
        flag |= O_RDWR;                     //设置为可读写的串口属性文件   
        if(is_block == 0)  
            flag |=O_NONBLOCK;              //若为0则表示以非阻塞方式打开   
      
        fd = open(dev_path,flag);               //打开设备文件   
        if(fd < 0)  
        {  
            perror("Open device file err:");  
            close(fd);  
            return -1;  
        }  
      
        /*打开设备文件后,下面开始设置波特率*/  
        set_speed(fd,speed);                //考虑到波特率可能被单独设置,所以独立成函数   
      
        /*设置奇偶校验*/  
        if(set_parity(fd) != 0)  
        {  
            perror("set parity error:");  
            close(fd);                      //一定要关闭文件,否则文件一直为打开状态   
            return -1;  
        }  
      
        return fd;  
    }  
    /*----------------------------------------------------------------------------- 
      函数名:      serial_send 
      参数:        int fd,char *str,unsigned int len 
      返回值:      发送成功返回发送长度,否则返回小于0的值 
      描述:        向fd描述符的串口发送数据,长度为len,内容为str 
     *-----------------------------------------------------------------------------*/  
    int serial_send(int fd,char *str,unsigned int len)  
    {  
        int ret;    
        
        if(len > strlen(str))                    //判断长度是否超过str的最大长度   
            len = strlen(str);  
      
        ret = write(fd,str,len);  
        if(ret < 0)  
        {  
            perror("serial send err:");  
            return -1;  
        } 
           
        return ret;  
    }  
      
    /*----------------------------------------------------------------------------- 
      函数名:      serial_read 
      参数:        int fd,char *str,unsigned int len,unsigned int timeout 
      返回值:      在规定的时间内读取数据,超时则退出,超时时间为ms级别 
      描述:        向fd描述符的串口接收数据,长度为len,存入str,timeout 为超时时间 
     *-----------------------------------------------------------------------------*/  
    int serial_read(int fd, char *str, unsigned int len, unsigned int timeout)  
    {  
        fd_set rfds;  
        struct timeval tv;  
        int ret;                                //每次读的结果   
        int sret;                               //select监控结果   
        int readlen = 0;                        //实际读到的字节数   
        char * ptr;  
      
        ptr = str;                          //读指针,每次移动,因为实际读出的长度和传入参数可能存在差异   
      
        FD_ZERO(&rfds);                     //清除文件描述符集合   
        FD_SET(fd,&rfds);                   //将fd加入fds文件描述符,以待下面用select方法监听   
      
        /*传入的timeout是ms级别的单位,这里需要转换为struct timeval 结构的*/  
        tv.tv_sec  = timeout / 1000;  
        tv.tv_usec = (timeout%1000)*1000;  
      
        /*防止读数据长度超过缓冲区*/  
        //if(sizeof(&str) < len)   
        //  len = sizeof(str);   
      
      
        /*开始读*/  
        while(readlen < len)  
        {  
            sret = select(fd+1,&rfds,NULL,NULL,&tv);        //检测串口是否可读   
      
            if(sret == -1)                              //检测失败   
            {  
                perror("select:");  
                break;  
            }  
            else if(sret > 0)                        //<SPAN style="WHITE-SPACE: pre"> </SPAN>//检测成功可读   
            {  
                ret = read(fd,ptr,1); //第三个参数为请求读取的字节数 
                if(ret < 0)  
                {  
                    perror("read err:");  
                    break;  
                }  
                else if(ret == 0)  
                    break;  
      
                readlen += ret;                             //更新读的长度   
                ptr     += ret;                             //更新读的位置   
            }  
            else                                          //超时   sret == 0 ,没填满buf也退出循环
            {  
                printf("timeout!
    ");  
                break;  
            }  
        }  
      
        return readlen;  
    }  
    
    int socket_read(int fd,char *str,unsigned int len,unsigned int timeout)
    {
        fd_set fdsr;
        struct timeval tv;
        int readlen = 0; 
        char * ptr; 
        int ret;
        ptr = str; 
        // initialize file descriptor set
        FD_ZERO(&fdsr);//每次循环都要清空
        FD_SET(fd, &fdsr);
        tv.tv_sec  = timeout / 1000;  
        tv.tv_usec = (timeout%1000)*1000;  
        while(readlen < len){
            ret = select(fd + 1, &fdsr, NULL, NULL, &tv);
            if (ret < 0) {
                perror("select");
                break;
            } else if (ret == 0) {
                printf("timeout
    ");
                break;
              }
            //每次申请读取8个字节,但事实上是按发送端每次发送的字符串长度来确定的,如果长度小于8,则每次读取实际长度,如果大于8,则读取8字节。
            //recv多少就从缓冲区中删除多少,剩下的数据可以在下次recv时得到
            //即使子线程挂起,也一直有数据可以读,数据不丢失,真正的接收数据是协议来完成的,存放在s的接收缓冲中。
            ret = recv(fd, ptr, 8, 0);//申请8个字节            
            if (ret <= 0) {//如果连接已中止,返回0。如果发生错误,返回-1
                printf("client close
    ");
                close(fd);
                FD_CLR(fd, &fdsr);
                fd = 0;
            } else {        
               readlen +=ret;
               ptr += ret; 
               //printf("the ret length is:%d
    ",readlen); 
            }
        }
        return readlen;
    }
    
    void *socket_thread(void *arg)
    {
        char sockbuf[SOCK_MAXLINE];
        int sreadlen;
        int freadlen;
        struct argument thread_arg;
        thread_arg = *(struct argument *)arg;
        memset(sockbuf, 0, sizeof(sockbuf));
        while(1)
        {        
            sreadlen = socket_read(thread_arg.sockfd, sockbuf, SOCK_MAXLINE-8, 1000);//为了防止缓存溢出,少读取一个字节
            printf("the sockbuf is:%s
    ", sockbuf);//打印出数据
            printf("the sockbuf length is:%d
    ",sreadlen); 
            freadlen = serial_send(thread_arg.fd,sockbuf,sreadlen); 
            printf("send %d bytes!
    ",freadlen);   
            memset(sockbuf, 0, sizeof(sockbuf));
            usleep(1);
        }
    
    
    }
    
    int main(int argc, char** argv)
    {
        //串口变量定义
        int fd;  
        //int ret;  
        //char str[]="hello linux serial!"; //字符串初始化 
        char serbuf[SER_MAXLINE]; 
        int readlen;
        // socket变量定义 
         int    sockfd; 
        struct sockaddr_in    servaddr;    
        
        //多线程
        pthread_t thread;
        int mret;
        struct argument arg;
        
        //判断输入是否正确输入参数
        if( argc != 2)
        {
            printf("usage: ./client <ipaddress>
    ");
            exit(0);
        }    
        //串口初始化
        //fd =  serial_init("/dev/ttyS0",115200,1);  
        //fd =  serial_init("/dev/ttyUSB0",115200,1);  
        fd = serial_init("/dev/ttyATH0", 115200,1);if(fd < 0)  
        {  
            perror("serial init err:");  
            return -1;  
        } 
        memset(serbuf, 0, sizeof(serbuf));
        //socket 初始化
        if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            printf("create socket error: %s(errno: %d)
    ", strerror(errno),errno);
            exit(0);
        }
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(10001);
        if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        {
            printf("inet_pton error for %s
    ",argv[1]);
            exit(0);
        }
        if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        {
            printf("connect error: %s(errno: %d)
    ",strerror(errno),errno);
            exit(0);
        }   
        printf("send msg to server: 
    ");
        
        //测试代码 
        //ret = serial_send(fd,str,22);  
        //printf("send %d bytes!
    ",ret); 
        
        //fgets(sendline, MAXLINE, stdin);//要处理
        
        /*if( send(sockfd, str, strlen(str), 0) < 0)
        {
            printf("send msg error: %s(errno: %d)
    ", strerror(errno), errno);
            exit(0);
        }*/
        
        //创建多线程,tcp转串口
        arg.fd=fd;
        arg.sockfd=sockfd;
        mret = pthread_create(&thread, NULL, socket_thread, (void *)(long)&arg);     
        if (mret != 0) 
        { 
            printf("Create thread failed
    "); 
            exit(mret); 
        }
        printf("Create thread
    "); 
        
        //串口转tcp
        while(1)
        {        
            readlen = serial_read(fd,serbuf,SER_MAXLINE,1000);//1s内如果数据没有装满buf,则读取完毕。 如果数据量大,则读取的速度也越快。 
            printf("the serbuf is :%s
    ",serbuf); 
            printf("the serbuf length is :%d
    ",readlen); 
            if( send(sockfd, serbuf, readlen, 0) < 0)
            {
                printf("send msg error: %s(errno: %d)
    ", strerror(errno), errno);
                exit(0);
            }
            memset(serbuf, 0, sizeof(serbuf));
            usleep(1);
        }
        //退出
        close(fd); 
        close(sockfd);
        exit(0);
    }

    二、编译

    1、 进入barrier_breaker目录,执行make package/utils/ttl_client/compile V=s

    三、配置

    安装好ipk包及其依赖包后,

    vi /etc/inittab

    注释最后一行。

  • 相关阅读:
    【知识强化】第六章 查找 6.4 散列(Hash)表
    【知识强化】第七章 排序 7.5 归并排序和基数排序
    【知识强化】第六章 查找 6.3 B树和B+树
    【知识强化】第五章 图 5.4 图的应用
    【知识强化】第五章 图 5.3 图的遍历
    linux的自启动服务脚本的(/etc/rc.d/init.d或者其链接/etc/init.d)
    shell文件包含
    shell输入输出重定向
    shell函数参数
    shell函数
  • 原文地址:https://www.cnblogs.com/smbx-ztbz/p/4530352.html
Copyright © 2011-2022 走看看