美文网首页
Android USB转串口通信

Android USB转串口通信

作者: 阿拉贡居民 | 来源:发表于2021-09-02 18:33 被阅读0次

    最近需要在Android板卡上面对接些串口控制的外设,比如,4G模块、呼吸灯、光感传感器等,这些都是串口指令控制。
    目前Android板卡只有一个串口,想再控制其他外设,只能通过usb转串口, 下面做个记录。

    java串口驱动

    在网上搜索了下android下面usb转串口驱动,有些直接用java实现,比如ch340, 我也根据网上资料用java把usb转串口跑通,
    但是如果换一个usb转串口芯片就不好使, 通过修改串口驱动源码,也没有调好。github上面都有源码,有需要的自己下载调试,这里不多说明。
    关于java实现usb转串口驱动的原理,还没来得及细研究,有时间再研究下!。

    参数博客:
    https://blog.csdn.net/tianruxishui/article/details/38338087
    https://blog.csdn.net/tangchao5206/article/details/76999475

    Android_CH34xUART官方驱动实例
    https://github.com/BestCoderXQX/Android_CH34xUARTDemo

    这个人反编译了CH34xUART驱动jar包源码开发, 省了我很多工作,本来准备自己反编译。
    https://github.com/HeHongdan/CH34xUART

    Kernel配置USB转串口驱动

    由于java串口驱动调试通了ch340,另外一个使用cp210, 没有调试通, 去silicon官网查看,他们只支持kenrel驱动,于是根据官网pdf文档,配置kernel驱动,测试了CH341, CP210X USB串口都没有问题。
    我这里把编译成模块,编译成ko后自己插入!

    +++ b/kernel/linux-4.9.y/arch/arm64/configs/hi3751v810_arm64_android_defconfig
    @@ -3019,7 +3019,10 @@ CONFIG_USB_STORAGE=y
     #
     # USB port drivers
     #
    -# CONFIG_USB_SERIAL is not set
    +CONFIG_USB_SERIAL=m
    +CONFIG_USB_SERIAL_GENERIC=y
    +CONFIG_USB_SERIAL_CH341=m
    +CONFIG_USB_SERIAL_CP210X=m
    

    串口测试程序

    这个串口操作接口封装挺好的,也分享出来。
    swserial.h

    #ifndef __SW_SERIAL_H_
    #define __SW_SERIAL_H_
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    int32_t sw_serial_open(const char *dev);
    void sw_serial_close(int fd);
    int32_t sw_serial_read(int fd, unsigned char *buf, int len, int timeout);
    int32_t sw_serial_write(int fd, unsigned char *buf, int len, int timeout);
    int32_t sw_serial_set_parity(int fd,int speed,int databits,int stopbits,int parity);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    swserial.c

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <pthread.h>
    #include <sys/select.h>
    #include <sys/ioctl.h>
    #include <termios.h>
    #include <android/log.h>
    
    #undef LOG_TAG
    #define LOG_TAG "swserial"
    
    #define USE_NON_ICANON
    
    typedef struct {
        int fd;
        char * dev;
    }sw_serial_t;
    #define MAX_SERIAL_NUM 3
    sw_serial_t m_sList[MAX_SERIAL_NUM] = {
        {-1, NULL},
        {-1, NULL},
        {-1, NULL}
    };
    
    int32_t sw_serial_open(const char *dev)
    {
        int fd, err, i;
        if( dev == NULL )
        {
            fprintf(stderr,"%s: Invalid parameter\n",__func__);
            return -1;
        }
        for( i=0; i<MAX_SERIAL_NUM; i++)
        {
            if( m_sList[i].fd >= 0 && m_sList[i].dev && strcmp(m_sList[i].dev, dev) == 0 )
            {
                fprintf(stderr,"[%s:%d] %s alread opened\n",__func__,__LINE__,dev);
                return m_sList[i].fd;
            }
        }
        for( i=0; i<MAX_SERIAL_NUM; i++)
        {
            if( m_sList[i].fd == -1 )
                break;
        }
    
        fd = open(dev, O_NOCTTY|O_NONBLOCK|O_RDWR);
        if( fd == -1 )
        {
            err = errno;
            fprintf(stderr,"open %s failed: %s\n",dev, strerror(err));
            return -1;
        }
    
        if( i == MAX_SERIAL_NUM )
        {
            i = 0;
            close(m_sList[i].fd);
            if( m_sList[i].dev )
                free(m_sList[i].dev);
            m_sList[i].dev = NULL;
        }
        m_sList[i].fd = fd;
        m_sList[i].dev = (char *)malloc(strlen(dev)+1);
        if( m_sList[i].dev )
            snprintf(m_sList[i].dev,sizeof(m_sList[i].dev), "%s", dev);
    
        return fd;
    }
    
    void sw_serial_close(int fd)
    {
        int i;
        close(fd);
        for( i=0; i<MAX_SERIAL_NUM; i++)
        {
            if( m_sList[i].fd == fd )
            {
                m_sList[i].fd = -1;
                if( m_sList[i].dev )
                    free( m_sList[i].dev );
                m_sList[i].dev = NULL;
                break;
            }
        }
    }
    
    int32_t sw_serial_read(int fd, unsigned char *buf, int len, int timeout)
    {
        int rsize, rlen=0, err;
        struct timeval tv;
        fd_set readfds;
        FD_ZERO(&readfds);
    
        tv.tv_sec = timeout/1000;
        tv.tv_usec = (timeout%1000)*1000;
        fprintf(stderr,"[%s:%d] fd:%d\n",__func__,__LINE__, fd);
    
    select_again:
        FD_SET(fd, &readfds);
        err = select( fd+1, &readfds, NULL, NULL, (tv.tv_sec>0 || tv.tv_usec>0)? (&tv):NULL );
        if( err == -1 )
        {
            fprintf(stderr,"select error: %s\n", strerror(errno));
            return -1;
        }
        else if( err )
        {
            if( FD_ISSET(fd, &readfds) )
            {
                if( (rsize = read( fd, buf+rlen, len-rlen)) > 0 )
                {
                    rlen += rsize;
                }
            }
            else
            {
                fprintf(stderr,"[%s:%d] select again\n",__func__,__LINE__);
                goto select_again;
            }
        }
        else
            fprintf(stderr,"[%s:%d] select timeout\n",__func__,__LINE__);
        buf[rlen] = 0;
        fprintf(stderr,"[%s:%d] read(%d bytes): [%s]\n",__func__,__LINE__, rlen, buf);
        return rlen;
    }
    
    int32_t sw_serial_write(int fd, unsigned char *buf, int len, int timeout)
    {
        int wsize = 0, err;
        struct timeval tv;
        fd_set writefds;
        FD_ZERO(&writefds);
    
        tv.tv_sec = timeout/1000;
        tv.tv_usec = (timeout%1000)*1000;
    
        fprintf(stderr,"[%s:%d] fd:%d\n",__func__,__LINE__, fd);
    write_again:
        FD_SET(fd, &writefds);
        err = select( fd+1, NULL, &writefds, NULL, (tv.tv_sec>0 || tv.tv_usec>0)? (&tv):NULL );
        if( err == -1 )
        {
            fprintf(stderr,"select error: %s\n", strerror(errno));
            return -1;
        }
        else if( err )
        {
            if( FD_ISSET(fd, &writefds) )
            {
                wsize = write( fd, buf, len);
            }
            else
                goto write_again;
        }
        return wsize;
    }
    
    static int speed_arr[] = { B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300,
        B38400, B19200, B9600, B4800, B2400, B1200, B300, };
    static int name_arr[] = {115200, 38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,
        19200,  9600, 4800, 2400, 1200,  300, };
    
    /**
     *  @brief 设置串口参数
     *  
     *  @param fd 串口句柄
     *  @param speed 波特率
     *  @param databits 数据位数
     *  @param stopbits 停止位位数
     *  @param parity 模式
     *  
     *  @return 设置成功返回0,失败返回-1
     */
    int sw_serial_set_parity(int fd,int speed,int databits,int stopbits,int parity)
    {
        int i;
        int is_speed_true = 0;
        struct termios options; 
        fprintf(stderr,"[%s:%d] fd:%d\n",__func__,__LINE__, fd);
        tcflush(fd, TCIOFLUSH);
        if  ( tcgetattr( (int)fd, &options )  !=  0)
        { 
            fprintf(stderr,"tcgetattr failed: %s\n",strerror(errno));
            return -1;  
        }
        for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {
            if  (speed == name_arr[i]) {
                is_speed_true = 1;
                break;
            }
        }
        if(is_speed_true == 1)
        {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);     
        }
        else
        {
            fprintf(stderr,"set speed failed: %s\n",strerror(errno));
            return -1;  
        }
        options.c_iflag = 0;
        options.c_oflag = 0;
    
        options.c_cflag &= ~CSIZE; 
        switch (databits) /*......*/
        {   
            case 5:     
                options.c_cflag |= CS5; 
                break;
            case 6:     
                options.c_cflag |= CS6; 
                break;
            case 7:     
                options.c_cflag |= CS7; 
                break;
            case 8:     
                options.c_cflag |= CS8;
                break;   
            default:    
                fprintf(stderr,"Unsupported data sizen"); 
                return -1;  
        }
        switch (parity) 
        {   
            case 'n':
            case 'N':    
                options.c_cflag &= ~PARENB;     /* Clear parity enable */
                options.c_iflag &= ~INPCK;      /* Disable parity checking */ 
                break;  
            case 'o':   
            case 'O':     
                options.c_cflag |= PARENB;      /* Enable parity */    
                options.c_cflag |= PARODD;      /* Parity for input and output is odd */
                options.c_iflag |= INPCK;       /* Enable input parity checking */ 
                break;  
            case 'e':  
            case 'E':   
                options.c_cflag |= PARENB;      /* Enable parity */    
                options.c_cflag &= ~PARODD;     /* Parity for input and output is even */ 
                options.c_iflag |= INPCK;       /* Enable input parity checking */
                break;
            case 'S': 
            case 's':  /*as no parity*/   
                options.c_cflag &= ~PARENB;
                options.c_cflag &= ~CSTOPB;
                break;  
            default:   
                fprintf(stderr,"Unsupported parityn");    
                return -1;  
        }
        /* .....*/  
        switch (stopbits)
        {   
            case 1:    
                options.c_cflag &= ~CSTOPB;  
                break;  
            case 2:    
                options.c_cflag |= CSTOPB;  
                break;
            default:    
                fprintf(stderr,"Unsupported stop bitsn");  
                return -1; 
        }
    
        //设置校验位
        switch(parity)
        {
            case 'n'://无奇偶校验
            case 'N':
                options.c_cflag &= ~PARENB;
                options.c_iflag &= ~INPCK;
                break;
            case 'o'://奇校验
            case 'O':
                options.c_cflag |= (PARODD | PARENB);
                options.c_iflag |= INPCK;
                break;
            case 'e'://偶校验
            case 'E':
                options.c_cflag |= PARENB;
                options.c_cflag &= ~PARODD;
                options.c_iflag |= INPCK;
                break;
            case 's'://空格
            case 'S':
                options.c_cflag &= ~PARENB;
                options.c_cflag &= ~CSTOPB;
                break;
            default ://默认无奇偶校验
                fprintf(stderr,"Unsupported parity bitsn");  
                return -1; 
        }
    
    #ifdef USE_NON_ICANON
        options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    #else
        options.c_lflag  &= ~( ECHO | ECHOE | ISIG);  /*Input*/
    #endif
        options.c_oflag  &= ~OPOST;   /*Output*/
    
        tcflush( (int)fd, TCIFLUSH );
    #ifdef USE_NON_ICANON
        options.c_cc[VTIME] = 10; /* ....15 seconds*/   
        options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
    #endif
        if (tcsetattr( (int)fd, TCSANOW, &options ) != 0)   
        { 
            fprintf(stderr,"tcgetattr failed: %s\n",strerror(errno));
            return -1;  
        }
    
        //fcntl((int)fd, F_SETFL, O_NDELAY); 
        return 0;
    
    }
    

    serial_main.c

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <stdlib.h>
    
    #include "swserial.h"
    
    
    static int m_serial_fd = -1;
    static int m_quit = 0;
    
    static void * abs_serial_read_proc(void *arg)
    {
        int ret = 0;
        char recvbuf[1024]= {0}; 
        while(!m_quit){
            memset(recvbuf, 0, sizeof(recvbuf));
            //ret = sw_serial_read(m_serial_fd, (unsigned char *)recvbuf, 1024, -1);    
            ret = read(m_serial_fd, (unsigned char *)recvbuf, 1024);    
            if (ret == 0)
                continue;
            if(ret>0){
                printf("recv[%d]: %s \n",ret, recvbuf);
            }
            usleep(200*1000);
        }
        pthread_exit(NULL);
    }
    
    int main(int argc, char*argv[])
    {
        char sendbuf[512] = {0};    
        int ret = -1;
        pthread_t read_id;
        void *status;
    
    
        m_serial_fd = sw_serial_open("/dev/ttyUSB0");
        if(m_serial_fd == -1)
        {
            printf(" /dev/ttyUSB0 open failed!\n");
            return -1;
        }
        ret = sw_serial_set_parity(m_serial_fd, 115200, 8, 1, 'n');
        if(ret != 0)
        {
            printf("seial set parity failed! \n");
            return -1;
        }
        
        //创建串口读线程
        ret = pthread_create(&read_id, NULL, abs_serial_read_proc, 0); 
        if(ret != 0)
        { 
            printf("pthread_create failed! \n");
            return -1;
        }
    
        //读取数据,发送给串口
        //__fpurge(stdin);
        printf("please input$:");
        while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
        {
            if(strncmp(sendbuf, "\n", 1) == 0)
            {
                printf("please input$: ");
                continue;
            }
            if(strncmp(sendbuf, "quit", 4) == 0)
            {
                printf("serial test quit!\n ");
                break;
            }
    
            sendbuf[strlen(sendbuf)-1] = '\r';
            sendbuf[strlen(sendbuf)] = '\n';
        
            //ret = sw_serial_write(m_serial_fd, (unsigned char*)sendbuf, strlen(sendbuf), 200);
            ret = write(m_serial_fd, (unsigned char*)sendbuf, strlen(sendbuf));
            printf("send[%d]: %s", ret , sendbuf );
    
            memset(sendbuf, 0 , sizeof(sendbuf));
        }
        
        m_quit = 1;
        pthread_join(read_id, &status);
        sw_serial_close(m_serial_fd);
        m_serial_fd = -1;
    
        return 0;
    }
    
    

    Android.mk

    LOCAL_PATH:= $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_PRELINK_MODULE := false
    LOCAL_SRC_FILES:= \
            swserial.c \
            serial_main.c
    
    LOCAL_MODULE_TAGS := optional
    #LOCAL_STATIC_LIBRARIES += 
    LOCAL_SHARED_LIBRARIES := \
        libutils\
        libcutils\
        liblog\
    LOCAL_C_INCLUDES := \
        $(LOCAL_PATH)
    
    #LOCAL_CLANG := false
    LOCAL_MODULE:= serial_test
    
    ALL_DEFAULT_INSTALLED_MODULES += $(LOCAL_MODULE)
    include $(BUILD_EXECUTABLE)
    

    相关文章

      网友评论

          本文标题:Android USB转串口通信

          本文链接:https://www.haomeiwen.com/subject/kmjcwltx.html