最近需要在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)
网友评论