当前位置:   article > 正文

QT Android 串口通信

QT Android 串口通信

目录

1. 实现Java层的USB串口通信

1.1 初始化与权限管理

1.2 获取设备列表并请求权限

1.3 打开串口并启动I/O管理器

2. 接收串口数据并通过JNI传递到C++

2.1 数据接收与打印

2.2 通过JNI传递数据到C++

3. 关闭串口

2. 实现C++层的JNI交互与Qt UI集成

2.1 创建 PortWidgets 类

2.2 初始化与刷新串口列表

PortWidgets.cpp 文件中构造函数的实现

2.3 使用JNI与Java层交互

刷新串口设备列表

连接并打开串口

2.4 处理接收到的串口数据

JNI 回调函数与数据处理

2.5 关闭窗口

本文将介绍如何在Android平台上通过Java代码实现USB串口通信,并通过JNI(Java Native Interface)将数据传递到C++层进行处理。这个过程涵盖了从权限管理、串口数据收发、到C++信号与槽的实现。 

完整代码:GitHub - TryTryTL/serialport

1. 实现Java层的USB串口通信

1.1 初始化与权限管理

首先,我们需要创建一个 UsbController 类来管理USB设备。这个类负责初始化 UsbManager,请求用户权限,并监听设备权限的授予情况。

  1. package org.qtproject.example;
  2. import android.app.PendingIntent;
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.hardware.usb.UsbDevice;
  8. import android.hardware.usb.UsbDeviceConnection;
  9. import android.hardware.usb.UsbManager;
  10. import android.util.Log;
  11. import com.hoho.android.usbserial.driver.UsbSerialDriver;
  12. import com.hoho.android.usbserial.driver.UsbSerialPort;
  13. import com.hoho.android.usbserial.driver.UsbSerialProber;
  14. import com.hoho.android.usbserial.util.SerialInputOutputManager;
  15. import java.io.IOException;
  16. import java.io.UnsupportedEncodingException;
  17. import java.util.ArrayList;
  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.concurrent.ExecutorService;
  21. import java.util.concurrent.Executors;
  22. public class UsbController implements SerialInputOutputManager.Listener {
  23. private Context context;
  24. private UsbManager usbManager;
  25. private PendingIntent permissionIntent;
  26. private UsbSerialPort pendingUsbSerialPort;
  27. private SerialInputOutputManager mSerialIoManager;
  28. private ExecutorService mExecutor = Executors.newSingleThreadExecutor();

在构造函数中,我们初始化了 UsbManager 并注册了一个 BroadcastReceiver,以便监听用户是否授予了对设备的访问权限。

  1. public UsbController(Context context) {
  2. this.context = context;
  3. this.usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
  4. this.permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent("org.qtproject.example.USB_PERMISSION"), 0);
  5. IntentFilter filter = new IntentFilter("org.qtproject.example.USB_PERMISSION");
  6. context.registerReceiver(usbPermissionReceiver, filter);
  7. }
1.2 获取设备列表并请求权限

我们提供了一个 getDeviceList 方法,返回当前连接的USB设备列表,并请求用户权限。

  1. public String[] getDeviceList() {
  2. HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
  3. String[] deviceNames = new String[usbDevices.size()];
  4. int index = 0;
  5. for (UsbDevice device : usbDevices.values()) {
  6. deviceNames[index++] = device.getDeviceName();
  7. usbManager.requestPermission(device, permissionIntent);
  8. }
  9. return deviceNames;
  10. }
1.3 打开串口并启动I/O管理器

当用户授予权限后,我们会打开USB串口,并启动 SerialInputOutputManager 以处理串口数据的收发。

  1. public void requestPermissionAndOpenPort(UsbSerialPort usbSerialPort, int baudRate, int dataBits, int stopBits, int parity) {
  2. UsbDevice device = usbSerialPort.getDriver().getDevice();
  3. if (usbManager.hasPermission(device)) {
  4. openSerialPort(usbSerialPort, baudRate, dataBits, stopBits, parity);
  5. } else {
  6. pendingUsbSerialPort = usbSerialPort;
  7. usbManager.requestPermission(device, permissionIntent);
  8. }
  9. }
  10. private void openSerialPort(UsbSerialPort usbSerialPort, int baudRate, int dataBits, int stopBits, int parity) {
  11. UsbDeviceConnection connection = usbManager.openDevice(usbSerialPort.getDriver().getDevice());
  12. if (connection != null) {
  13. try {
  14. usbSerialPort.open(connection);
  15. usbSerialPort.setParameters(baudRate, dataBits, stopBits, parity);
  16. Log.d("USB", "Serial port opened successfully.");
  17. startIoManager(usbSerialPort);
  18. } catch (IOException e) {
  19. Log.e("USB", "Error opening serial port: " + e.getMessage());
  20. }
  21. } else {
  22. Log.e("USB", "Could not open connection - permission might be required.");
  23. }
  24. }
  25. private void startIoManager(UsbSerialPort usbSerialPort) {
  26. if (usbSerialPort != null) {
  27. mSerialIoManager = new SerialInputOutputManager(usbSerialPort, this);
  28. mExecutor.submit(mSerialIoManager);
  29. }
  30. }

2. 接收串口数据并通过JNI传递到C++

2.1 数据接收与打印

当串口接收到数据时,onNewData 方法会被回调。我们可以在此将数据转换为字符串并打印出来,确认接收是否成功。

  1. @Override
  2. public void onNewData(final byte[] data) {
  3. try {
  4. String receivedData = new String(data, "UTF-8");
  5. Log.d("USB", "Received data: " + receivedData);
  6. } catch (UnsupportedEncodingException e) {
  7. Log.e("USB", "UTF-8 encoding is not supported", e);
  8. }
  9. onDataReceivedFromJava(data);
  10. if (onDataReceivedListener != null) {
  11. onDataReceivedListener.onDataReceived(data);
  12. }
  13. }
2.2 通过JNI传递数据到C++

onDataReceivedFromJava 是一个本地方法,负责将接收到的数据传递到C++层。

    public native void onDataReceivedFromJava(byte[] data);
3. 关闭串口

在不再需要使用串口时,我们提供了一个 closeSerialPort 方法来停止I/O管理器并关闭串口。

  1. public void closeSerialPort() {
  2. stopIoManager();
  3. if (pendingUsbSerialPort != null) {
  4. try {
  5. pendingUsbSerialPort.close();
  6. Log.d("USB", "Serial port closed.");
  7. } catch (IOException e) {
  8. Log.e("USB", "Error closing serial port: " + e.getMessage());
  9. }
  10. pendingUsbSerialPort = null;
  11. }
  12. }

2. 实现C++层的JNI交互与Qt UI集成

现在我们将进一步探讨如何在C++中处理这些数据,并将其与Qt的UI集成。

2.1 创建 PortWidgets

PortWidgets 类是Qt中的一个自定义控件,它负责管理串口通信,并通过JNI与Java层进行交互。我们首先在构造函数中初始化UI并设置相关的串口参数。

PortWidgets.h 文件结构

  1. #ifndef PORTWIDGETS_H
  2. #define PORTWIDGETS_H
  3. #include <QWidget>
  4. #include <QSerialPort>
  5. #include <unordered_map>
  6. #include <QAndroidJniObject>
  7. namespace Ui {
  8. class PortWidgets;
  9. }
  10. class PortWidgets : public QWidget
  11. {
  12. Q_OBJECT
  13. public:
  14. explicit PortWidgets(QWidget *parent = nullptr);
  15. ~PortWidgets();
  16. // JNI 方法,用于从 Java 接收数据
  17. void onDataReceivedFromJava(const QByteArray &data);
  18. signals:
  19. void receivedata(QByteArray data);
  20. private slots:
  21. void on_ptn_refresh_clicked();
  22. void receive_data();
  23. void on_ptn_conn_clicked();
  24. void on_pushButton_clicked();
  25. public:
  26. void write_data(QByteArray writedata);
  27. private:
  28. Ui::PortWidgets *ui;
  29. QSerialPort *myPort; // 串口指针
  30. QAndroidJniObject javaUsbController;
  31. static PortWidgets *instance;
  32. std::unordered_map<int, QSerialPort::BaudRate> baudRateMap = {
  33. { 0, QSerialPort::Baud1200 },{ 1, QSerialPort::Baud2400 },{ 2, QSerialPort::Baud4800 },
  34. { 3, QSerialPort::Baud9600 },{ 4, QSerialPort::Baud19200 },{ 5, QSerialPort::Baud38400 },
  35. { 6, QSerialPort::Baud57600 },{ 7, QSerialPort::Baud115200 }
  36. };
  37. std::unordered_map<int, QSerialPort::DataBits>dataMap = {
  38. { 0, QSerialPort::Data5 },{ 1, QSerialPort::Data6 },{ 2, QSerialPort::Data7 },{ 3, QSerialPort::Data8 }
  39. };
  40. std::unordered_map<int, QSerialPort::Parity>parityMap = {
  41. { 0, QSerialPort::NoParity },{ 1, QSerialPort::OddParity },{ 2, QSerialPort::EvenParity }
  42. };
  43. std::unordered_map<int, QSerialPort::StopBits> stopMap = {
  44. {0, QSerialPort::OneStop}, {1, QSerialPort::OneAndHalfStop}, {2, QSerialPort::TwoStop}
  45. };
  46. };
  47. #endif // PORTWIDGETS_H
2.2 初始化与刷新串口列表

PortWidgets 的构造函数中,我们初始化了UI组件,并为不同的串口参数(波特率、数据位、校验位、停止位)设置了选项。此外,我们还调用了 on_ptn_refresh_clicked() 函数来刷新并显示当前可用的串口列表。

PortWidgets.cpp 文件中构造函数的实现
  1. PortWidgets* PortWidgets::instance = nullptr;
  2. PortWidgets::PortWidgets(QWidget *parent)
  3. : QWidget(parent)
  4. , ui(new Ui::PortWidgets)
  5. {
  6. // 在构造函数中初始化静态实例指针
  7. instance = this;
  8. ui->setupUi(this);
  9. this->setWindowTitle("PortWidgets");
  10. this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
  11. // 设置串口参数
  12. QStringList baudratelist = {"1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"};
  13. ui->CmbBaud->addItems(baudratelist);
  14. ui->CmbBaud->setCurrentIndex(3);
  15. QStringList datalist = {"5", "6", "7", "8"};
  16. ui->CmbDataBits->addItems(datalist);
  17. ui->CmbDataBits->setCurrentIndex(3);
  18. QStringList checklist = {"No", "Even", "Odd"};
  19. ui->CmbParity->addItems(checklist);
  20. ui->CmbParity->setCurrentIndex(0);
  21. QStringList stoplist = {"1", "1.5", "2"};
  22. ui->CmbStopBits->addItems(stoplist);
  23. ui->CmbStopBits->setCurrentIndex(0);
  24. // 刷新串口列表
  25. on_ptn_refresh_clicked();
  26. myPort = new QSerialPort(this);
  27. connect(myPort, &QSerialPort::readyRead, this, &PortWidgets::receive_data);
  28. }
  29. PortWidgets::~PortWidgets()
  30. {
  31. // 在析构函数中清除静态实例指针
  32. instance = nullptr;
  33. delete ui;
  34. }
2.3 使用JNI与Java层交互

我们通过JNI与Java层进行交互,获取串口设备列表并打开串口。在 on_ptn_refresh_clicked()on_ptn_conn_clicked() 方法中,我们使用 QAndroidJniObject 来调用Java方法。

刷新串口设备列表
  1. void PortWidgets::on_ptn_refresh_clicked() {
  2. QAndroidJniObject javaUsbController = QAndroidJniObject("org/qtproject/example/UsbController",
  3. "(Landroid/content/Context;)V",
  4. QtAndroid::androidContext().object());
  5. QAndroidJniObject result = javaUsbController.callObjectMethod("getAllSerialPort", "()Ljava/util/List;");
  6. QAndroidJniEnvironment env;
  7. jobject listObject = result.object<jobject>();
  8. jclass listClass = env->FindClass("java/util/List");
  9. jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I");
  10. jmethodID getMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
  11. int size = env->CallIntMethod(listObject, sizeMethod);
  12. QStringList deviceList;
  13. for (int i = 0; i < size; i++) {
  14. jobject serialPortObject = env->CallObjectMethod(listObject, getMethod, i);
  15. QAndroidJniObject usbDevice = QAndroidJniObject::fromLocalRef(serialPortObject).callObjectMethod("getDevice", "()Landroid/hardware/usb/UsbDevice;");
  16. QString deviceName = usbDevice.callObjectMethod("getDeviceName", "()Ljava/lang/String;").toString();
  17. deviceList << deviceName;
  18. qDebug() << "Serial Port found: " << deviceName;
  19. }
  20. ui->CmPortlist->clear(); // 清除旧的列表项
  21. ui->CmPortlist->addItems(deviceList); // 添加新的端口列表
  22. if (!deviceList.isEmpty()) {
  23. ui->CmPortlist->setCurrentIndex(0); // 默认选择第一个端口
  24. }
  25. }
连接并打开串口
  1. void PortWidgets::on_ptn_conn_clicked()
  2. {
  3. int portIndex = 0;
  4. int baudRate = 9600; // 示例波特率
  5. int dataBits = 8; // 示例数据位
  6. int stopBits = 1; // 示例停止位
  7. int parity = 0; // 示例校验位,无校验
  8. QAndroidJniObject javaUsbController = QAndroidJniObject("org/qtproject/example/UsbController",
  9. "(Landroid/content/Context;)V",
  10. QtAndroid::androidContext().object());
  11. QAndroidJniObject serialPorts = javaUsbController.callObjectMethod("getAllSerialPort", "()Ljava/util/List;");
  12. QAndroidJniEnvironment env;
  13. jobject listObject = serialPorts.object<jobject>();
  14. jobject serialPortObject = env->CallObjectMethod(listObject, env->GetMethodID(env->GetObjectClass(listObject), "get", "(I)Ljava/lang/Object;"), portIndex);
  15. javaUsbController.callMethod<void>("requestPermissionAndOpenPort",
  16. "(Lcom/hoho/android/usbserial/driver/UsbSerialPort;IIII)V",
  17. serialPortObject,
  18. baudRate,
  19. dataBits,
  20. stopBits,
  21. parity);
  22. }
2.4 处理接收到的串口数据

当从Java层接收到串口数据时,通过JNI回调函数将数据传递到C++层,并通过Qt的信号槽机制将数据传递到UI或其他需要处理的地方。

JNI 回调函数与数据处理
  1. extern "C" JNIEXPORT void JNICALL
  2. Java_org_qtproject_example_UsbController_onDataReceivedFromJava(JNIEnv *env, jobject, jbyteArray data) {
  3. jsize length = env->GetArrayLength(data);
  4. jbyte* byteArray = env->GetByteArrayElements(data, nullptr);
  5. QByteArray receivedData((const char*)byteArray, length);
  6. if (PortWidgets::instance) {
  7. PortWidgets::instance->onDataReceivedFromJava(receivedData);
  8. }
  9. env->ReleaseByteArrayElements(data, byteArray, 0);
  10. }
  11. void PortWidgets::onDataReceivedFromJava(const QByteArray &data)
  12. {
  13. emit receivedata(data);
  14. }
2.5 关闭窗口

为了确保应用程序的UI响应,我们在关闭按钮的点击事件中调用了 close() 方法,以关闭当前窗口。

  1. void PortWidgets::on_pushButton_clicked()
  2. {
  3. this->close();
  4. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1021396
推荐阅读
相关标签
  

闽ICP备14008679号