Qt for Android : 使用libusb做CH340x串口傳輸的底層USB庫

簡介

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 <QSerialPort>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 <QDebug>#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		64CH340X::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 fullint 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 <QJniObject>
#include <QJniEnvironment>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_ANDROIDQJniObject 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);}
#elsefor (QSerialPortInfo &info : QSerialPortInfo::availablePorts()){names.append(info.portName());}
#endifreturn 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串口傳輸

其他

沁恒微 官網CH34x Linux驅動
沁恒微 官網CH34x Android驅動

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20578.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20578.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20578.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringBoot注解--10--@Bean,對象注入的三種方法

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 Bean一、如何使用方法注解注意Bean 的命名規則&#xff0c;當沒有設置 name 屬性時&#xff0c;那么 bean 默認的名稱就是方法名&#xff0c;當設置了 name 屬性之后…

解析Java中1000個常用類:Runnable 類,你學會了嗎?

在 Java 編程中,處理并發和多線程是一個重要的主題。為了簡化多線程編程,Java 提供了多種工具和類,其中最基本的一個工具就是 Runnable 接口。 Runnable 接口為創建和管理線程提供了一種標準的方式。本文將詳細介紹 Runnable 接口的定義、實現原理、應用場景,并通過示例展…

33【Aseprite 作圖】樹——拆解

1 樹葉 畫樹葉真累啊&#xff0c;可以先畫一個輪廓&#xff0c;細節一點點修 2 1 2 &#xff1b;2 2 2 &#xff08;橫著橫&#xff09;&#xff0c;這樣一點點畫樹葉 填充顏色&#xff0c;用了噴霧工具 2 樹干部分 輪廓部分&#xff0c;左邊的是3 3 3 &#xff1b;上下都是…

網頁音頻提取在線工具有哪些 網頁音頻提取在線工具下載

別再到處去借會員賬號啦。教你一招&#xff0c;無視版權和地區限制&#xff0c;直接下載網頁中的音頻文件。沒有復雜的操作步驟&#xff0c;也不用學習任何代碼。只要是網頁中播放的音頻文件&#xff0c;都可以把它下載到本地保存。 一、網頁音頻提取在線工具有哪些 市面上的…

【數據結構】二叉樹:簡約和復雜的交織之美

專欄引入&#xff1a; 哈嘍大家好&#xff0c;我是野生的編程萌新&#xff0c;首先感謝大家的觀看。數據結構的學習者大多有這樣的想法&#xff1a;數據結構很重要&#xff0c;一定要學好&#xff0c;但數據結構比較抽象&#xff0c;有些算法理解起來很困難&#xff0c;學的很累…

Transformer中的位置編碼PE(position encoding)

Transformer中的位置編碼PE(position encoding) 1.提出背景 transformer模型的attention機制并沒有包含位置信息&#xff0c;即一句話中詞語在不同的位置時在transformer中是沒有區別的 2.解決背景 給encoder層和decoder層的輸入添加了一個額外的向量Positional Encoding&a…

平移數據c++

題目描述 將a數組中第一個元素移到數組末尾,其余數據依次往前平移一個位置。 輸入 第一行為數組a的元素個數n&#xff1b; 第二行為n個小于1000的正整數。 輸出 平移后的數組元素&#xff0c;每個數用一個空格隔開。 樣例輸入 10 1 2 3 4 5 6 7 8 9 10 樣例輸出 2 3 …

【專利 超音速】一種光伏檢測系統

申請號CN202410053901.0公開號&#xff08;公開&#xff09;CN118032774A申請日2024.01.12申請人&#xff08;公開&#xff09;超音速人工智能科技股份有限公司發明人&#xff08;公開&#xff09;張俊峰(總); 葉長春(總); 許春夏 摘要 本發明公開一種光伏檢測系統&#xff0…

iotdb時序庫在火電設備鍋爐場景下的實踐【原創文字,IoTDB社區可進行使用與傳播】

一.概述 1.1 說明 本文章主要介紹iotdb數據庫在電站鍋爐工業場景下&#xff0c;對輔助智能分析與預警的使用介紹。 【原創文字&#xff0c;IoTDB社區可進行使用與傳播】 1.2 項目背景 隨著人工智能算法在電力領域的發展&#xff0c;以及燃煤鍋爐設備精細化調整需求的增加&…

Java基礎八股

Java基礎八股 Java語言Java語言有什么特點Java與C區別Java如何實現跨平臺JVMvsJDKvsJRE標識符和關鍵字的區別是什么自增自減運算符移位運算符continue,break,return的區別是什么final,finally,finalize的區別final關鍵字的作用時什么 變量 Java語言 Java語言有什么特點 Java是…

LED燈編程:一步步探索光的魔法

LED燈編程&#xff1a;一步步探索光的魔法 在數字時代&#xff0c;LED燈早已超越了傳統的照明功能&#xff0c;成為編程與創意結合的完美載體。那么&#xff0c;LED燈怎么編程呢&#xff1f;本文將分四個方面、五個方面、六個方面和七個方面&#xff0c;帶您走進LED燈編程的奇…

如何在Python中管理內存

在Python中&#xff0c;內存管理主要是由解釋器自動處理的&#xff0c;這包括對象的分配和回收。Python使用引用計數和垃圾回收機制來管理內存&#xff0c;這大大簡化了開發者的工作&#xff0c;因為他們通常不需要手動管理內存。 然而&#xff0c;盡管Python自動管理內存&…

數據結構——經典鏈表OJ(二)

樂觀學習&#xff0c;樂觀生活&#xff0c;才能不斷前進啊&#xff01;&#xff01;&#xff01; 我的主頁&#xff1a;optimistic_chen 我的專欄&#xff1a;c語言 點擊主頁&#xff1a;optimistic_chen和專欄&#xff1a;c語言&#xff0c; 創作不易&#xff0c;大佬們點贊鼓…

chatgpt之api的調用問題

1.調用api過程中&#xff0c;出現如下報錯內容 先寫一個測試樣例 import openaiopenai.api_key "OPEN_AI_KEY" openai.api_base"OPEN_AI_BASE_URL" # 是否需要base根據自己所在地區和key情況進行completion openai.ChatCompletion.create(model"g…

【intro】GNN中異構圖(heterogeneous graph)綜述

本篇博客內容是讀兩篇論文&#xff0c;兩篇論文連接如下&#xff1a; Heterogeneous graph neural networks analysis: a survey of techniques, evaluations and applications A Survey on Heterogeneous Graph Embedding: Methods, Techniques, Applications and Sources …

瓦羅蘭特國際服 外服游玩教程 瓦羅蘭特外服下載注冊游玩指南

瓦羅蘭特國際服 外服游玩教程 瓦羅蘭特外服下載注冊游玩指南 瓦羅蘭特作為當今游戲圈頂流的一款熱門FPS。游戲&#xff0c;作為拳頭游戲公司劃時代的一款游戲。游戲不僅延續了傳統FPS游戲的玩法&#xff0c;還添加許多新玩法&#xff0c;這也是游戲可以吸引大批量玩家的原因之…

Flink面試整理-對Flink的高級特性如CEP(復雜事件處理)、狀態后端選擇和調優等有所了解

Apache Flink 提供了一系列高級特性,使其成為一個強大的實時數據處理框架,特別適用于復雜的數據處理場景。其中,復雜事件處理(CEP)、狀態后端的選擇和調優是其中重要的幾個方面。 復雜事件處理(CEP) CEP 概念:CEP 是用于在數據流中識別復雜模式的技術。它允許用戶指定事…

基于電導增量MPPT控制算法的光伏發電系統simulink建模與仿真

目錄 1.課題概述 2.系統仿真結果 3.核心程序與模型 4.系統原理簡介 5.完整工程文件 1.課題概述 基于電導增量MPPT控制算法的光伏發電系統simulink建模與仿真。輸出MPPT跟蹤后的系統電流&#xff0c;電壓以及功率。 2.系統仿真結果 3.核心程序與模型 版本&#xff1a;MAT…

cocos creator 3.x實現手機虛擬操作桿

簡介 在許多移動游戲中&#xff0c;虛擬操縱桿是一個重要的用戶界面元素&#xff0c;用于控制角色或物體的移動。本文將介紹如何在Unity中實現虛擬操縱桿&#xff0c;提供了一段用于移動控制的代碼。我們將討論不同類型的虛擬操縱桿&#xff0c;如固定和跟隨&#xff0c;以及如…

Go常見語法題目解析

1、寫出下面代碼輸出內容。 package mainimport ("fmt" )func main() {defer_call() }func defer_call() {defer func() { fmt.Println("打印前") }()defer func() { fmt.Println("打印中") }()defer func() { fmt.Println("打印后")…