Qt for Android自带的串口方案并没有适用在高的API版本中, 会出现permission denied的访问问题, 所以就需要使用Android API, 也就是在CPP中使用JNI方式进行调用, 为了开发的方便, 使用libusb库作为替代的底层usb传输是比较合适的, 这里基于 Qt for android 串口库使用 这篇所说的java 编写的ch340x的库简单修改成调用libusb进行串口通讯的方法验证。
别说一上来就是代码, 其实翻译有啥步骤, 上来就是撸代码
ch340x.h
#ifndef CH340X_H
#define CH340X_H
#include "libusb/libusb.h"
#include
class CH340X
{
public:
enum ControlLine { RTS, CTS, DTR, DSR, CD, RI };
enum DataBits
{
DATABITS_5 = 5,
DATABITS_6 = 6,
DATABITS_7 = 7,
DATABITS_8 = 8,
};
enum Parity
{
PARITY_NONE = 0,
PARITY_ODD = 1,
PARITY_EVEN = 2,
PARITY_MARK = 3,
PARITY_SPACE = 4,
};
enum STOPBITS
{
STOPBITS_1 = 1,
STOPBITS_1_5 = 3,
STOPBITS_2 = 2,
};
CH340X(int fd=-1);
~CH340X();
inline bool isValid() { return m_isValid; }
int send(unsigned char *src, int length, int timeout);
private:
int controlOut(int request, int value, int index);
int controlIn(int request, int value, int index, unsigned char *buffer, uint16_t length);;
int setControlLines();
int getStatus(char &status);
int checkState(int request, int value,
char *expected,
unsigned int length);
int setBaudRate(int baudRate);
int setParameters(int baudRate, int dataBits, int stopBits, int parity);
int init_ch34x(int fd);
private:
bool m_isValid = false;
struct libusb_device_handle *devh = NULL;
};
#endif // CH340X_H
ch340x.cpp
#include "ch340x.h"
#include
#include "libusb/libusb.h"
static int LCR_ENABLE_RX = 0x80;
static int LCR_ENABLE_TX = 0x40;
static int LCR_MARK_SPACE = 0x20;
static int LCR_PAR_EVEN = 0x10;
static int LCR_ENABLE_PAR = 0x08;
static int LCR_STOP_BITS_2 = 0x04;
static int LCR_CS8 = 0x03;
static int LCR_CS7 = 0x02;
static int LCR_CS6 = 0x01;
static int LCR_CS5 = 0x00;
static int GCL_CTS = 0x01;
static int GCL_DSR = 0x02;
static int GCL_RI = 0x04;
static int GCL_CD = 0x08;
static int SCL_DTR = 0x20;
static int SCL_RTS = 0x40;
int USB_TIMEOUT_MILLIS = 5000;
int DEFAULT_BAUD_RATE = 9600;
bool dtr = false;
bool rts = false;
#define USB_TYPE_VENDOR 0x40
#define USB_DIR_OUT 0x0
// #define EP_DATA_IN (0x2|LIBUSB_ENDPOINT_IN)
#define EP_DATA_OUT (0x2|LIBUSB_ENDPOINT_OUT)
#define EP_INTR (1 | LIBUSB_ENDPOINT_IN)
#define EP_DATA (2 | LIBUSB_ENDPOINT_IN)
#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
#define USB_RQ 0x04
#define INTR_LENGTH 64
CH340X::CH340X(int fd)
{
m_isValid = (init_ch34x(fd) == 0);
}
CH340X::~CH340X()
{
// if (recv_bulk_transfer)
// {
// libusb_cancel_transfer(recv_bulk_transfer);
// libusb_free_transfer(recv_bulk_transfer);
// }
if (devh)
{
libusb_release_interface(devh, 0);
libusb_close(devh);
libusb_exit(NULL);
}
}
int CH340X::controlOut(int request, int value, int index)
{
return libusb_control_transfer(devh, CTRL_OUT, request, value, index, 0, 0, USB_TIMEOUT_MILLIS);
}
int CH340X::controlIn(int request, int value, int index, unsigned char *buffer, uint16_t length)
{
return libusb_control_transfer(devh, CTRL_IN, request, value, index, buffer, length, USB_TIMEOUT_MILLIS);
}
int CH340X::setControlLines()
{
return controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0);
}
int CH340X::getStatus(char &status)
{
char buffer[2];
int ret = controlIn(0x95, 0x0706, 0, (unsigned char*)buffer, 2);
status = buffer[0];
return ret;
}
int CH340X::checkState(int request, int value,
char *expected,
unsigned int length)
{
char *buffer = new char[length];
int ret = controlIn(request, value, 0, (unsigned char*)buffer, length);
if (ret < 0) {
return ret;
}
if (ret != length) {
// qDebug("Expected " [ length] + " bytes, but get " + ret + " [" + msg + "]");
return ret;
}
for (int i = 0; i < length; i++)
{
if (expected[i] == -1)
{
continue;
}
int current = buffer[i] & 0xff;
if (expected[i] != current)
{
// qDebug("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]");
return -1;
}
}
return 0;
}
int CH340X::setBaudRate(int baudRate)
{
long factor;
long divisor;
if (baudRate == 921600)
{
divisor = 7;
factor = 0xf300;
}
else
{
long BAUDBASE_FACTOR = 1532620800;
int BAUDBASE_DIVMAX = 3;
factor = BAUDBASE_FACTOR / baudRate;
divisor = BAUDBASE_DIVMAX;
while ((factor > 0xfff0) && divisor > 0)
{
factor >>= 3;
divisor--;
}
if (factor > 0xfff0) { // 波特率不支持
return -1;
}
factor = 0x10000 - factor;
}
divisor |= 0x0080; // else ch341a waits until buffer full
int val1 = (int) ((factor & 0xff00) | divisor);
int val2 = (int) (factor & 0xff);
// Log.d(TAG, String.format("baud rate=%d, 0x1312=0x%04x, 0x0f2c=0x%04x", baudRate, val1, val2));
int ret = controlOut(0x9a, 0x1312, val1);
if (ret < 0) {
return ret;
// throw new IOException("Error setting baud rate: #1)");
}
ret = controlOut(0x9a, 0x0f2c, val2);
if (ret < 0) {
return ret;
// throw new IOException("Error setting baud rate: #2");
}
return 0;
}
int CH340X::setParameters(int baudRate, int dataBits, int stopBits, int parity)
{
if(baudRate <= 0)
{
return -1;
// throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
}
setBaudRate(baudRate);
int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
switch (dataBits) {
case DATABITS_5:
lcr |= LCR_CS5;
break;
case DATABITS_6:
lcr |= LCR_CS6;
break;
case DATABITS_7:
lcr |= LCR_CS7;
break;
case DATABITS_8:
lcr |= LCR_CS8;
break;
default:
return -1;
// throw new IllegalArgumentException("Invalid data bits: " + dataBits);
}
switch (parity) {
case PARITY_NONE:
break;
case PARITY_ODD:
lcr |= LCR_ENABLE_PAR;
break;
case PARITY_EVEN:
lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN;
break;
case PARITY_MARK:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE;
break;
case PARITY_SPACE:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN;
break;
default:
return -1;
// throw new IllegalArgumentException("Invalid parity: " + parity);
}
switch (stopBits) {
case STOPBITS_1:
break;
case STOPBITS_1_5:
return -1;
// throw new UnsupportedOperationException("Unsupported stop bits: 1.5");
case STOPBITS_2:
lcr |= LCR_STOP_BITS_2;
break;
default:
return -1;
// throw new IllegalArgumentException("Invalid stop bits: " + stopBits);
}
int ret = controlOut(0x9a, 0x2518, lcr);
if (ret < 0) {
return ret;
// throw new IOException("Error setting control byte");
}
return 0;
}
int CH340X::init_ch34x(int fd)
{
int r = 0;
libusb_context *ctx = NULL;
r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
if (r != LIBUSB_SUCCESS) {
qDebug("libusb_set_option failed: %d\n", r);
return r;
}
r = libusb_init(&ctx);
if (r < 0) {
qDebug("libusb_init failed: %d\n", r);
return r;
}
r = libusb_wrap_sys_device(ctx, (intptr_t)fd, &devh);
if (r < 0) {
qDebug("libusb_wrap_sys_device failed: %d\n", r);
return r;
} else if (devh == NULL) {
qDebug("libusb_wrap_sys_device returned invalid handle\n");
return r;
}
r = libusb_claim_interface(devh, 0); // 独占
if (r < 0) {
qDebug("usb_claim_interface error %d\n", r);
return r;
}
char stateCmd[2] = {static_cast<char>(-1), 0};
checkState(0x5f, 0, stateCmd, 2);
if (controlOut(0xa1, 0, 0) < 0) {
return -1;
// throw new IOException("Init failed: #2");
}
setBaudRate(DEFAULT_BAUD_RATE);
checkState(0x95, 0x2518, stateCmd, 2);
if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
return -1;
// throw new IOException("Init failed: #5");
}
stateCmd[1] = -1;
checkState(0x95, 0x2518, stateCmd, 2);
if (controlOut(0xa1, 0x501f, 0xd90a) < 0) {
return -1;
// throw new IOException("Init failed: #7");
}
setBaudRate(DEFAULT_BAUD_RATE);
setControlLines();
checkState(0x95, 0x2518, stateCmd, 2);
r = setParameters(9600, DATABITS_8, STOPBITS_1, PARITY_NONE);
return r;
}
int CH340X::send(unsigned char *src, int length, int timeout)
{
if (!m_isValid)
return -1;
int realSendLength = 0;
return libusb_bulk_transfer(devh, EP_DATA_OUT, src, length, &realSendLength, timeout);
}
mainwindows.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ch340x.h"
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->btnRefreshPortNames, &QPushButton::clicked,
this, [=]()
{
ui->cbPortNames->clear();
ui->cbPortNames->addItems(getSerialPorts());
});
connect(ui->btnTest, &QPushButton::clicked,
this, &MainWindow::test);
emit ui->btnRefreshPortNames->clicked(); // 借势初始化
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::logger(const QString &text)
{
ui->textEdit->append(text);
}
QList<QString> MainWindow::getSerialPorts()
{
QList<QString> names;
#ifdef Q_OS_ANDROID
QJniObject usbService = QJniObject::getStaticObjectField("android/content/Context",
"USB_SERVICE",
"Ljava/lang/String;");
if (!usbService.isValid())
{
logger("fail to get usb service");
return names;
}
QJniObject activity = QJniObject(QNativeInterface::QAndroidApplication::context());
QJniObject usbManager = activity.callObjectMethod("getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
usbService.object<jstring>());
if (!usbManager.isValid())
{
logger("fail to get usb manager");
return names;
}
QJniObject usbDeviceListHashMap = usbManager.callObjectMethod("getDeviceList",
"()Ljava/util/HashMap;");
QJniObject devListKeySet = usbDeviceListHashMap.callObjectMethod("keySet",
"()Ljava/util/Set;");
QJniObject devListIter = devListKeySet.callObjectMethod("iterator",
"()Ljava/util/Iterator;");
jint devListSize = usbDeviceListHashMap.callMethod<jint>("size", "()I");
QJniObject usbDevObj;
QJniObject usbDevObjIter;
int vid = 0, pid = 0;
QString devName;
for (int i = 0; i < devListSize; ++i)
{
usbDevObjIter = devListIter.callObjectMethod("next",
"()Ljava/lang/Object;");
usbDevObj = usbDeviceListHashMap.callObjectMethod("get",
"(Ljava/lang/Object;)Ljava/lang/Object;",
usbDevObjIter.object());
vid = usbDevObj.callMethod<jint>("getVendorId", "()I");
pid = usbDevObj.callMethod<jint>("getProductId", "()I");
devName = usbDevObj.callMethod<jstring>("getDeviceName", "()Ljava/lang/String;").toString();
logger(QString("Name: %1, VID: %2, PID: %3").arg(devName).arg(vid).arg(pid));
names.append(devName);
}
#else
for (QSerialPortInfo &info : QSerialPortInfo::availablePorts())
{
names.append(info.portName());
}
#endif
return names;
}
void MainWindow::test()
{
QJniObject str = QJniObject::fromString(ui->cbPortNames->currentText());
jboolean res = QJniObject::callStaticMethod<jboolean>(
"usb/USBListActivity",
"requestUSBPermission",
"(Ljava/lang/String;)Z",
str.object<jstring>());
if (!res)
{
logger("permission denied");
return;
}
QJniObject usbService = QJniObject::getStaticObjectField("android/content/Context",
"USB_SERVICE",
"Ljava/lang/String;");
if (!usbService.isValid())
{
logger("fail to get usb service");
return;
}
QJniObject activity = QJniObject(QNativeInterface::QAndroidApplication::context());
QJniObject usbManager = activity.callObjectMethod("getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
usbService.object<jstring>());
if (!usbManager.isValid())
{
logger("fail to get usb manager");
return;
}
QJniObject usbDeviceListHashMap = usbManager.callObjectMethod("getDeviceList",
"()Ljava/util/HashMap;");
QJniObject devListKeySet = usbDeviceListHashMap.callObjectMethod("keySet",
"()Ljava/util/Set;");
QJniObject devListIter = devListKeySet.callObjectMethod("iterator",
"()Ljava/util/Iterator;");
jint devListSize = usbDeviceListHashMap.callMethod<jint>("size", "()I");
QJniObject usbDevObj;
QJniObject usbDevObjIter;
QString devName;
QJniObject selectedUsbDevice;
for (int i = 0; i < devListSize; ++i)
{
usbDevObjIter = devListIter.callObjectMethod("next",
"()Ljava/lang/Object;");
usbDevObj = usbDeviceListHashMap.callObjectMethod("get",
"(Ljava/lang/Object;)Ljava/lang/Object;",
usbDevObjIter.object());
devName = usbDevObj.callMethod<jstring>("getDeviceName", "()Ljava/lang/String;").toString();
if (devName != ui->cbPortNames->currentText())
{
continue;
}
selectedUsbDevice = usbDevObj;
break;
}
if (!selectedUsbDevice.isValid())
{
logger("device not found");
return;
}
QJniObject usbConnection = usbManager.callObjectMethod("openDevice",
"(Landroid/hardware/usb/UsbDevice;)Landroid/hardware/usb/UsbDeviceConnection;",
selectedUsbDevice.object());
if (!usbConnection.isValid())
{
logger("fail to get usb connection");
return;
}
jint fd = usbConnection.callMethod<jint>("getFileDescriptor", "()I");
CH340X ch340X(fd);
const char buf[] = {"Hello World!!!\n"};
if (!ch340X.isValid())
{
logger("fail to init ch340x.");
return;
}
ch340X.send((unsigned char*)buf, sizeof(buf), 1000);
// unrooted_usb_description(fd);
logger("libusb test finished7");
}
Qt for Android :使用 libusb做ch340x串口传输