应用界面编写(十四)

一. 介绍QT

接下来我们会在Qt Creater来进行界面的编写,并且在荔枝派中运行。那么我们有必要了解一下Qt到底是什么呢?它又为什么可以在荔枝派中运行呢?

QT是一个跨平台的应用程序和用户界面框架,用于开发具有图形界面的软件。而QT的跨平台原理则是基于“一次编码,处处编译”的策略。这意味着开发者可以使用相同的源代码在不同的平台上编译,以生产适应各个平台的应用程序。QT框架通过封装底层操作系统的差异,为开发者提供了统一的API接口,从而实现跨平台开发。

而QT可以对不同平台的编译器和环境进行区分,例如,QT使用宏定义来区分不同的操作系统和编译器环境,如Linux、MSVC、MINGW等,从而在编译时选择合适的实现。

二. QT移植和编译执行文件

全志D1-H,官方SDK Tina2.0 移植 QT5.15 | 全志在线开发者论坛 (aw-ol.com)

2.1 下载源码

点击下载QT5.15.4

wget https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/qt/5.15/5.15.4/single/qt-everywhere-opensource-src-5.15.4.tar.xz

tar zxvf qt-everywhere-opensource-src-5.15.4.tar.xz
cd qt-everywhere-opensource-src-5.15.4

2.2 环境配置

添加交叉编译配置文件qt-everywhere-src-5.15.4/qtbase/mkspecs/linux-riscv64-gnu-g++/qmake.conf,内容如下:

# qmake configuration for building with riscv64-unknown-linux-gnu-g++no
MAKEFILE_GENERATOR      = UNIX
CONFIG                 += incremental
QMAKE_INCREMENTAL_STYLE = sublib

include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)

# modifications to g++.conf
QMAKE_CC                = riscv64-unknown-linux-gnu-gcc   # 需要使用的交叉编译工具链的名称
QMAKE_CXX               = riscv64-unknown-linux-gnu-g++
QMAKE_LINK              = riscv64-unknown-linux-gnu-g++ 
QMAKE_LINK_SHLIB        = riscv64-unknown-linux-gnu-g++
QMAKE_LIBS              = -latomic

# modifications to linux.conf
QMAKE_AR                = riscv64-unknown-linux-gnu-ar cqs
QMAKE_OBJCOPY           = riscv64-unknown-linux-gnu-objcopy
QMAKE_NM                = riscv64-unknown-linux-gnu-nm -P
QMAKE_STRIP             = riscv64-unknown-linux-gnu-strip
load(qt_config)

 添加编译配置文件qt-everywhere-src-5.15.4/qtbase/mkspecs/linux-riscv64-gnu-g++/qplatformdefs.h,内容如下:

/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the qmake spec of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "../linux-g++/qplatformdefs.h"

修改源码中一个缺失的头文件包含,位于 qt-everywhere-src-5.15.4/qtdeclarative/src/qmldebug/qqmlprofilerevent_p.h :52行,添加如下内容:

#include <limits>

在QT源码根目录创建 configure 自动配置文件

vi autoConfigure.sh
./configure \
    -prefix ./riscv-qt \
    -opensource \
    -confirm-license \
    -release \
    -strip \
    -shared \
    -xplatform linux-riscv64-gnu-g++ \
    -optimized-qmake \
    -c++std c++11 \
    --rpath=no \
    -pch \
    -skip qt3d \
    -skip qtactiveqt \
    -skip qtandroidextras \
    -skip qtcanvas3d \
    -skip qtconnectivity \
    -skip qtdatavis3d \
    -skip qtdoc \
    -skip qtgamepad \
    -skip qtlocation \
    -skip qtmacextras \
    -skip qtnetworkauth \
    -skip qtpurchasing \
    -skip qtremoteobjects \
    -skip qtscript \
    -skip qtscxml \
    -skip qtsensors \
    -skip qtspeech \
    -skip qtsvg \
    -skip qttools \
    -skip qttranslations \
    -skip qtwayland \
    -skip qtwebengine \
    -skip qtwebview \
    -skip qtwinextras \
    -skip qtx11extras \
    -skip qtxmlpatterns \
    -make libs \
    -make examples \
    -nomake tools \
    -nomake tests \
    -gui \
    -widgets \
    -dbus-runtime \
    --glib=no \
    --iconv=no \
    --pcre=qt \
    --zlib=qt \
    -no-openssl \
    --freetype=qt \
    --harfbuzz=qt \
    -no-opengl \
    -linuxfb \
    --xcb=no \
    -tslib \
    --libpng=qt \
    --libjpeg=qt \
    --sqlite=qt \
    -plugin-sql-sqlite \
    -I/home/allwinner/LicherPI/tina-d1-h/tina_d1_open_v2/out/d1-h-nezha/staging_dir/target/usr/include/ \
    -I/home/allwinner/LicherPI/tina-d1-h/tina_d1_open_v2/out/d1-h-nezha/staging_dir/target/usr/include/allwinner/ \
    -I/home/allwinner/LicherPI/tina-d1-h/tina_d1_open_v2/out/d1-h-nezha/staging_dir/target/usr/include/allwinner/include/ \
    -L/home/allwinner/LicherPI/tina-d1-h/tina_d1_open_v2/out/d1-h-nezha/staging_dir/target/usr/lib/ \
    -recheck-all

使用简化配置

试图使用最简化的配置选项来排除参数问题。例如:

复制代码

./configure -prefix /path/to/install/qt -opensource -confirm-license -release

如果这成功了,逐步添加其他参数以找出引发问题的参数。

2.3 交叉编译QT5文件

进入QT源码根目录,运行刚刚配置的环境。

source ./autoConfigure.sh

若配置成功会显示以下信息

image-20220529224209489

开始编译,具体核心数按照自己的配置指定,预留 8G 空间已足够使用。

make -j2

我按照以上配置一次编译成功,没有遇见错误。

编译成功后执行

make install

安装目录在qt-everywhere-src-5.15.4/qtbase/bin/riscv-qt

riscv-qt 就是刚刚配置文件中-prefix ./riscv-qt指定的目录。

2.4 移植

将该目录移动到DockRV开发板上,具体方法可以 tar 打包后通过 adb push 或者SFTP发送。

创建环境配置文件 qt-path

echo begin
export QT_HOME=/root/riscv-qt

export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_QPA_FB_DRM=1
export QT_QPA_GENERIC_PLUGINS=evdevkeyboard
export QT_QPA_EVDEV_KEYBOARD_PARAMETERS=/dev/input/event0

#export QT_QPA_GENERIC_PLUGINS=evdevmouse
#export QT_QPA_EVDEV_MOUSE_PARAMETERS=/dev/input/event1

export QT_QPA_GENERIC_PLUGINS=evdevtouch
export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event2

export QT_PLUGIN_PATH=$QT_HOME/plugins
export LD_LIBRARY_PATH=/lib:/usr/lib:$QT_HOME/lib

2.5 问题解决

2.5.1 链接断开

报错链接断开了,所有.so文件都断开了?

while loading shared libraries: /lib64/libgcc_s.so.1: file too short_error while loading shared libraries file too shor-CSDN博客

就改回蓝色了。 

2.5.2 报错缺少链接文件

root@TinaLinux:~/bin/riscv-qt/examples/gui/analogclock# ./analogclock
./analogclock: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory

检查 qt-path 的 LIB 配置, 或者是少了这些链接文件

libatomic.so,libatomic.so.1,libatomic.so.1.2.0

QT时钟程序报不能加载.so库 | 全志在线开发者论坛 (aw-ol.com)

添加如下链接文件,并且建立链接 

2.5.3 GCC版本报错 

CentOS 安装QT SDK qt-everywhere-src-5.15.0.tar.xz时报错-CSDN博客

GCC版本为4.8.5,需要升级GCC

2.5.4 qmake编译.pro文件

qtbase目录下,执行命令

$(QMAKE) -o Makefile 目录/.pro

QMAKE         = /home/allwinner/LicherPI/tina-d1-h/QT/qt-everywhere-src-5.15.4/qtbase/bin/qmake

/home/allwinner/LicherPI/tina-d1-h/QT/qt-everywhere-src-5.15.4/qtbase/bin/qmake -o Makefile apply_1/apply_1.pro

 .pro文件添加如下:

QMAKE_PROJECT_NAME = apply_1

# install

target.path = $$[QT_INSTALL_EXAMPLES]/apply_1/apply_1

INSTALLS += target

三. GPIO

bool GPIO::initialize()
{
    if (m_initialized) {
        return true; // Already initialized
    }

    // Export GPIO pin
    if (!exportPin()) {
        return false;
    }

    m_initialized = true;
    return true;
}

bool GPIO::exportPin()
{
    QFile file("/sys/class/gpio/export");
    if (file.open(QIODevice::WriteOnly))
    {
        QTextStream stream(&file);
        stream << m_pin;
        file.flush();
        file.close();
        return true;
    }
    qWarning() << "Failed to export GPIO pin:" << m_pin;
    return false;
}

bool GPIO::unexportPin()
{
    QFile file("/sys/class/gpio/unexport");
    if (file.open(QIODevice::WriteOnly))
    {
        QTextStream stream(&file);
        stream << m_pin;
        file.close();
        return true;
    }
    qWarning() << "Failed to unexport GPIO pin:" << m_pin;
    return false;
}

bool GPIO::setDirection(const QString &direction)
{
    QFile file(m_path + "direction");
    if (file.open(QIODevice::WriteOnly))
    {
        QTextStream stream(&file);
        stream << direction;
        file.close();
        return true;
    }
    qWarning() << "Failed to set direction for GPIO pin:" << m_pin;
    return false;
}

bool GPIO::setValue(int value)
{
    QFile file(m_path + "value");
    if (file.open(QIODevice::WriteOnly))
    {
        QTextStream stream(&file);
        stream << value;
        file.close();
        return true;
    }
    qWarning() << "Failed to set value for GPIO pin:" << m_pin;
    return false;
}

int GPIO::getValue()
{
    QFile file(m_path + "value");
    if (file.open(QIODevice::ReadOnly))
    {
        QTextStream stream(&file);
        int value;
        stream >> value;
        file.close();
        return value;
    }
    qWarning() << "Failed to get value for GPIO pin:" << m_pin;
    return -1;
}
PG15(207)GPIO输出控制继电器,控制风机,高电平吸合
PD20(116)GPIO输出控制485读写模式
PD9(105)GPIO输入,外部上拉读上限位
PD10(106)GPIO输入,外部上拉读下限位
PD12(108)GPIO输入,外部上拉读水位
PD13(109)GPIO输出控制继电器,来控制压缩机,高电平吸合
PD15(111)GPIO输出控制继电器,来控制水泵,高电平吸合
PD6(102)GPIO输入,外部上拉中间限位
PD11(107)GPIO输出控制继电器,底盘上升,高电平吸合
PD16(112)GPIO输出控制继电器,底盘下降,高电平吸合
PD17(113)GPIO输出控制固态继电器,来控制水加热(已接220v),低电平吸合

3.1 GPIO输出

实际上有些需要注意的地方,我们在改变 LED 的状态时,需要先去读取 LED 的状态,

PG15(207)GPIO输出控制继电器,控制风机,高电平吸合
PD15(111)GPIO输出控制继电器,来控制水泵,高电平吸合
PD17(113)GPIO输出控制固态继电器,来控制水加热(已接220v),低电平吸合
PD13(109)GPIO输出控制继电器,来控制压缩机,高电平吸合
PD11(107)GPIO输出控制继电器,底盘上升,高电平吸合
PD16(112)GPIO输出控制继电器,底盘下降,高电平吸合

 3.2 GPIO输入

PD9(105)GPIO输入,外部上拉读上限位
PD10(106)GPIO输入,外部上拉读下限位
PD12(108)GPIO输入,外部上拉读水位

通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该 GPIO 的操作接口从内核空间暴露到用户空间,GPIO 的操作接口包括 direction 和 value 等,direction 控制 GPIO 方向,而 value 可控制 GPIO 输出或获得 GPIO 输入。文件 IO 方式操作 GPIO,使用到了4个函数 open、close、read、write。

首先,看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入:

Device Drivers -> 
        GPIO Support ->
                /sys/class/gpio/… (sysfs interface)。

/sys/class/gpio 的使用说明

如果是在已经适配好的 Linux 内核上,那么相信已经有了完成的 gpiochip,可以在用户空间 /sys/class/gpio 目录下看到如下文件:

嵌入式Linux:Qt5+触摸屏+点灯(使用sysfs控制gpio)_qt gpio-CSDN博客

# cd /sys/class/gpio
# ls
export     gpiochip0  unexport

#define BEEP_PATH        "/sys/class/gpio/gpio36"

/* QButtonGroup */
bgbeep = new QButtonGroup(this);

connect(bgbeep, SIGNAL(buttonClicked(int)), this, SLOT(execCmd(int)));
    
void MainWindow::execCmd(int id)
{
    QString path = BEEP_PATH;
   /* 由于system函数只能用char类型,所以进行了转换 */
    /* export */
    QString cmd_export = "echo 36 > /sys/class/gpio/export";
    QByteArray cmdby_export = cmd_export.toLatin1();
    char* charCmd_export = cmdby_export.data();
    /* config */
    QString cmd_dir = "echo out >"+ path +"/direction";
    QByteArray cmdby_dir = cmd_dir.toLatin1();
    char* charCmd_dir = cmdby_dir.data();
    /* 关 */
    QString cmd_off = "echo 0 >"+ path +"/value";
    QByteArray cmdby_off = cmd_off.toLatin1();
    char* charCmd_off = cmdby_off.data();
    /* 开 */
    QString cmd_on = "echo 1 >"+ path +"/value";
    QByteArray cmdby_on = cmd_on.toLatin1();
    char* charCmd_on = cmdby_on.data();
	/* 导出GPIO */
    system(charCmd_export);
	/* 设置GPIO的输入输出状态 */
    system(charCmd_dir);

    switch (id) {
    case 0:
        system(charCmd_off);
        break;
    case 1:
        system(charCmd_on);
        break;
    }
}

四. 摄像头

V4L2是视频设备的驱动框架,为上层访问底层的视频视频设备,提供统一的接口,dev目录产生,USB摄像头常用,FFMPEG,使用V4L2库、完成对图片的捕捉,并且保存为一张图片,依照Tina SDK开发框架,我们代码床啊金在,

USB摄像头拍照Demo - D1-H (aw-ol.com)

https://blog.csdn.net/qq_41873311/article/details/125209866

V4L2编程之USB摄像头采集jpeg图像

首先是打开摄像头设备;
查询设备的属性或功能;
设置设备的参数,譬如像素格式、 帧大小、 帧率;
申请帧缓冲、 内存映射;
帧缓冲入队;
开启视频采集;
帧缓冲出队、对采集的数据进行处理;
处理完后,再次将帧缓冲入队,往复;
结束采集。

五. UART

//CO2模块
void Serial::serial_Open()
{
             CO2_Data.resize(6);

            // 删除旧的串口对象(如果存在)
            if (serial) {
                delete serial;
            }

            serial = new QSerialPort();
            serial->setPortName("/dev/ttyS1"); // 你的串口名称
            serial->setBaudRate(QSerialPort::Baud9600);
            serial->setDataBits(QSerialPort::Data8);
            serial->setParity(QSerialPort::NoParity);
            serial->setStopBits(QSerialPort::OneStop);
            serial->setFlowControl(QSerialPort::NoFlowControl);

            // 打开串口,只允许读
            if (serial->open(QIODevice::ReadOnly)) {
                qDebug() << "UART_1 opened successfully.";
            } else {
                qDebug() << "Failed to open UART_1";
            }

            // 连接信号和槽
        connect(serial, &QSerialPort::readyRead, this, &Serial::handleReadyRead);
}

void Serial::handleReadyRead()
{
    QByteArray data = serial->readAll();
    qDebug() << "Received data:" << data;

    // 处理接收到的数据
    for (char byte : data) {
        if (index < CO2_Data.size()) {
            CO2_Data[index++] = byte; // 使用 QByteArray 的赋值方法
        }

        if (index >= CO2_Data.size()) {
            // 校验数据的正确性
            if (CO2_Data[0] == 0x2C &&
                CO2_Data[5] == (uint8_t)(CO2_Data[0] + CO2_Data[1] + CO2_Data[2] + CO2_Data[3] + CO2_Data[4]))
            {
                // 更新UI,确保在主线程中更新 UI
                QMetaObject::invokeMethod(ui->co2, "setText", Qt::QueuedConnection,
                                          Q_ARG(QString, QString::number(CO2_Data[1] * 256 + CO2_Data[2])));

                // 打印调试信息
                qDebug() << CO2_Data[0] << " " << CO2_Data[1] << " " << CO2_Data[2] << " "
                         << CO2_Data[3] << " " << CO2_Data[4] << " " << CO2_Data[5];
            }

            // 重置索引
            index = 0;
        }
    }
}

二氧化碳模块--波特率:9600;模块型号:JW01-CO2-V2.2;

TXD是5V,但如果我们用3.3V供电,但我们D1-H只能承受3.3V,因此我们只能供电3.3V

数据位8位,停止位1位,无校验

一秒钟检测一次CO2,自带了数据检测。

下图可知,一共是六个字节,一个是固定的2C,通过这个来使得获取的数据位对齐

第二个和第三个字节可以通过公式来得到检测到的CO2浓度

然后第三个和第五个也是固定的,0x03和0xFF

最后一个字节是校验位,也就是前五个字节加起来,要转位8位的数据格式,例如uint8_t,等于第六个字节的时候,我们得到的数据才是正确的

JW01虽然是需要5V供电,但是3.3V也可以带动,不过预热速度会比5V稍慢一些

我们只需要接收JW01的信息而不需要给他发送数据,因为JW01固定发送的是6字节的数据,因此我们使用容量为6个uint8_t的数组来存放数据。在接收中断函数里面,按照接收的顺序把收到的数据放入到数组里,但是要多一个判断,只有当我们准备存放在数值的第一位并且此时接收的数据为0x2C的时候(参考前文的数据包格式),JW01的发送速率是一秒发一次。也就是说正常情况下我们数组里的数据是一秒更新一次。接收之后我们显示检测的数据,CO2的浓度为数组第二个元素乘上256再加上数值第三个元素,单位为ppm。并且之前也说了,六个字节的最后一个字节是校验位,前五个字节加起来要等于第六个字节,需要注意的是在比较的时候需要将比较结果转换为8个bit的数据,这样才能正确比较。

【STM32F103】JW01-CO2-V2.2二氧化碳检测模块(USART)_jw01二氧化碳传感器-CSDN博客

六. RS485

//RS485
void Serial::serial_485_Open()
{
    // 删除旧的串口对象(如果存在)
    if (serial_485) {
        delete serial_485;
    }

    serial_485 = new QSerialPort(this);
    serial_485->setPortName("/dev/ttyS4"); // 你的串口名称
    serial_485->setBaudRate(QSerialPort::Baud9600);
    serial_485->setDataBits(QSerialPort::Data8);
    serial_485->setParity(QSerialPort::NoParity);
    serial_485->setStopBits(QSerialPort::OneStop);
    serial_485->setFlowControl(QSerialPort::NoFlowControl);

    // 打开串口
    if (serial_485->open(QIODevice::ReadWrite)) {
        qDebug() << "UART_4 opened successfully.";
        sendRequest();
    } else {
        qDebug() << "Failed to open UART_4";
    }

    connect(serial_485, &QSerialPort::readyRead, this, &Serial::handleReadyRead_485);
}
void Serial::sendRequest()
{
    QByteArray request;
    request.append(static_cast<char>(0x01));  // 从站地址
    request.append(static_cast<char>(0x03));  // 功能码
    request.append(static_cast<char>(0x00));  // 起始地址高字节
    request.append(static_cast<char>(0x02));  // 起始地址低字节
    request.append(static_cast<char>(0x00));  // 寄存器数量高字节
    request.append(static_cast<char>(0x02));  // 寄存器数量低字节

    // 计算 CRC
    uint crc = CRC16(request);
    request.append(static_cast<char>(crc & 0xFF));     // CRC 低位
    request.append(static_cast<char>((crc >> 8) & 0xFF));  // CRC 高位

    serial_485->write(request);
    if (serial_485->waitForBytesWritten(1000)) {
        qDebug() << "Request sent successfully.";
    } else {
        qDebug() << "Failed to send request.";
    }
}

void Serial::handleReadyRead_485()
{
    QByteArray response = serial_485->readAll();
    processResponse(response);
}
void Serial::processResponse(const QByteArray &response)
{
    if (response.size() != 9)// 长度应为9
    {
        qDebug() << "Invalid response length.";
        return;
    }

     // 提取数据
    unsigned char temperatureH = response[3];
    unsigned char temperatureL = response[4];
    unsigned char humidityH = response[5];
    unsigned char humidityL = response[6];
    unsigned char crcL = response[7];
    unsigned char crcH = response[8];

    // 计算 CRC
    QByteArray data = response.left(7);  // 取前7个字节进行CRC校验(不包括 CRC 字节)
    uint calculatedCRC = CRC16(data);

    // 校验 CRC
    if (calculatedCRC == ((crcH << 8) | crcL)) {
        qDebug() << "CRC valid!";
        qDebug() << "Temperature:" << ((temperatureH << 8) | temperatureL);
        qDebug() << "Humidity:" << ((humidityH << 8) | humidityL);
    } else {
        qDebug() << "CRC invalid!";
    }
}
uint Serial::CRC16(const QByteArray &data)
{
    unsigned char uchCRCHi = 0xFF;  // 高CRC字节初始化
           unsigned char uchCRCLo = 0xFF;  // 低CRC字节初始化
           uint uIndex;

           static const unsigned char auchCRCHi[] = {
               // CRC 查找表的高字节部分
               0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
               0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
               0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
               0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
               0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
               0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
               0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
               0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
               0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
               0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
               0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
               0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
               0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
               0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
               0x00, 0xC1, 0x81, 0x40
           };

           static const unsigned char auchCRCLo[] = {
               // CRC 查找表的低字节部分
               0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
               0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
               0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
               0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
               0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
               0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
               0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
               0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
               0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
               0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
               0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
               0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
               0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
               0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
               0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
               0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
               0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D,
               0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A,
               0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
               0x41, 0x81, 0x80, 0x40
           };

           for (int i = 0; i < data.size(); ++i)
           {
               uIndex = uchCRCHi ^ static_cast<unsigned char>(data[i]);  // 计算CRC
               uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
               uchCRCLo = auchCRCLo[uIndex];
           }

           return (uchCRCHi << 8) | uchCRCLo;
}

void Serial::sendAddressChangeRequest()
{
    QByteArray request;

    // 构造请求数据
    request.append(static_cast<char>(0x00));  // 地址(主机地址)
    request.append(static_cast<char>(0x10));  // 功能码(写寄存器)
    request.append(static_cast<char>(0x00));  // 写入位置高位
    request.append(static_cast<char>(0x00));  // 写入位置低位
    request.append(static_cast<char>(0x00));  // 操作数高位(这里假设为0)
    request.append(static_cast<char>(0x01));  // 操作数低位(当前地址为0x01)
    request.append(static_cast<char>(0x02));  // 字节长度(修改地址需要2字节)
    request.append(static_cast<char>(0x00));  // 写入内容高位(新地址高位)
    request.append(static_cast<char>(0x02));  // 写入内容低位(新地址低位)

    // 计算CRC
    uint crc = CRC16(request);
    request.append(static_cast<char>(crc & 0xFF));     // CRC 低位
    request.append(static_cast<char>((crc >> 8) & 0xFF));  // CRC 高位

    serial_485->write(request);
    if (serial_485->waitForBytesWritten(1000)) {  // 等待最多3000毫秒
        qDebug() << "Address change request sent successfully.";
    } else {
        qDebug() << "Failed to send address change request.";
    }
}

步骤

  • 设置 RS485 通信:配置串口。
  • 发送请求:发送读取数据的命令。
  • 接收响应:读取并解析变送器返回的数据。
  • 校验数据:使用 CRC16 校验响应数据的有效性。
PD20(116)GPIO输出控制485读写模式

;半双工,半双工可以进行一主多从的通信;

逻辑1:以两线间的电压差为+(2~6)V来表示

逻辑0:以两线间的电压差为-(2~6)V来表示

B-RS-L30  光照传感器模块

1). 串行接口:波特率 9600, 数据位 8 位,停止位 1 位,无校验位;

2). 设备地址:默认地址模式 0x01;(“0x**”表示十六进制数)

THM-V6 温湿度模块

在RS485网络中使用MODBUS协议进行通信,MODBUS是一种用于工业自动化的串行通信协议,可以在RS485或RS232物理层上运行。

发送请求:发送的报文是'01 03 00 02 00 02 65 CB',其中'01'是从设备地址,'03'是功能码,'00 0 2'是起始地址,'00 0 2'是读取的寄存器数量,'65 CB'是CRC校验码。

接收数据并处理:发送器返回数据格式为'01 03 04 温度H 温度H 湿度L 湿度L CRC_L CRC_H',你需要读取这些字节并进行CRC校验。

CRC函数计算方法

预置一个16位的寄存器为十六进制FFFF,

把第一个8位二进制数据与16位的CRC寄存器的低8位相异或,

七. WIFI

void Serial::wifi_Connect(int *flag_t, const QString &command) {
//    if (flag == nullptr) {
//        qCritical() << "[Error] Flag pointer is null! Cannot proceed with WiFi connection.";
//        return;
//    }

    *flag_t = this->flag;
      qDebug() << "this->flag;" << this->flag;
    // 如果进程还在运行,等待其结束
    if (process && process->state() != QProcess::NotRunning) {
        qWarning() << "[Warning] Previous process is still running! Waiting for it to finish. Command: " << command;
        process->waitForFinished();  // 等待之前的进程结束
    }

    // 创建一个新的 QProcess 对象
    process = QSharedPointer<QProcess>::create(this);
    qDebug() << "[Info] Created a new QProcess for command: " << command;
    // 启动外部命令
    process->start(command);

    // 检查进程是否成功启动
    if (!process->waitForStarted()) {
        qCritical() << "[Error] Failed to start process for command: " << command;
      //  *flag = 0;
        return; // 退出函数,防止进一步操作
    } else {
        qDebug() << "[Info] Process started successfully. Command: " << command;
    }

    // 连接信号和槽
    connect(process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(handleStandardOutput()));
    connect(process.data(), SIGNAL(readyReadStandardError()), this, SLOT(handleStandardError()));
   // connect(process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));

    // 等待进程完成
    if (!process->waitForFinished()) {
        qWarning() << "Process did not finish correctly";
        process->deleteLater(); // 使用 deleteLater 释放内存
        return;
    }

}

void Serial::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
    qDebug() << "[Info] Process finished. Exit code:" << exitCode << ", Exit status:" << exitStatus;

    if (exitCode == 0) {
        qDebug() << "[Success] Process completed successfully.";
    } else {
        qWarning() << "[Failure] Process failed. Exit code: " << exitCode << ", Exit status: " << exitStatus;
    }

    // 检查 process 是否有效
    if (!process) {
        qCritical() << "[Error] Process object is null. It might have been destroyed elsewhere!";
        return;
    }

    // 在释放之前,检查进程状态
    if (process->state() != QProcess::NotRunning) {
        qWarning() << "[Warning] Process is still running! Waiting for it to finish.";
        if (!process->waitForFinished(5000)) {  // 等待进程结束(最多 5 秒)
            qCritical() << "[Critical] Process did not finish in time!";
        }
    }

    // 断开所有信号连接,确保不会访问被销毁的对象
    disconnect(process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(handleStandardOutput()));
    disconnect(process.data(), SIGNAL(readyReadStandardError()), this, SLOT(handleStandardError()));
    disconnect(process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));

    // 使用延时机制释放 QProcess 对象,避免在销毁时出现竞争条件
    QTimer::singleShot(10, this, SLOT(resetProcess()));
}

void Serial::resetProcess() {
    if (process && process->state() == QProcess::NotRunning) {
        qDebug() << "[Info] Process has been reset.";
        process.reset();
    } else {
        qWarning() << "[Warning] Process is still running or already null!";
    }
}


void Serial::handleStandardOutput() {
    if (process) {
        QString output = process->readAllStandardOutput();
        qDebug() << "[Info] Standard Output received from process:" << output;


        if (output.contains("Wifi connect ap : Success!")) {
            qDebug() << "[Success] WiFi connected to the specified network.";
            this->flag = 1;  // WiFi连接成功
            qDebug() <<"wifi_yesorno:"<<this->flag;
        }
//        else {
//            qWarning() << "[Warning] WiFi did not connect. Full output log: " << output;

//            this->flag = 0;  // WiFi连接失败
//        }

    } else {
        qCritical() << "[Error] Process object is null! Cannot read standard output.";
    }
}

void Serial::handleStandardError() {
    if (process) {
        QString error = process->readAllStandardError();
        qCritical() << "[Error] Standard Error received from process: " << error;
    } else {
        qCritical() << "[Error] Process object is null! Cannot read standard error.";
    }
}

Tina Linux SDK中已经内置了XR829的驱动并且以及启用,但是其默认适配的是全志官方的哪吒D1-H开发板,并不一定能直接在我们的开发板上使用。

Tina提供了一套简易方便的WI-FI STA 测试套件:wifi_connect_ap_test,使用这个命令可以设置连接的AP的SSID和密码,

在f1c100s上跑MySQL5.1.73 移植记录 / 全志 SOC / WhyCan Forum(哇酷开发者社区)

荔枝派Zero V3s开发板入坑记录 (TF/SD卡启动)(主线Linux,主线u-boot) / 全志 SOC / WhyCan Forum(哇酷开发者社区)

关于arm开发板远程访问mysql数据库 / 全志 SOC / WhyCan Forum(哇酷开发者社区)

【EVB-335X-II试用体验】之QT远程访问MySql数据库(交叉编译MySql QT客户端驱动程序及功能测试) - 盈鹏飞嵌入式 - 电子技术论坛 - 广受欢迎的专业电子论坛! (elecfans.com)

八. PWM0/PD16

#include "pwm.h"
#include <QFile>
#include <QTextStream>
#include <QDebug>
PWM::PWM()
{
}

void PWM::PWMController(int pwmChip, int pwmChannel) {
    pwmChipPath = QString("/sys/class/pwm/pwmchip%1/export").arg(pwmChip);
    pwmPath = QString("/sys/class/pwm/pwmchip%1/pwm%2/").arg(pwmChip).arg(pwmChannel);
}

bool PWM::exportPWM()
{
    QFile file(pwmChipPath);
    if (!file.open(QIODevice::WriteOnly)) {
        qWarning() << "Failed to open file for writing:" << pwmChipPath;
        return false;
    }

    QTextStream stream(&file);
    stream << 2;
    file.close();

    // Verify if the PWM channel is exported by checking the existence of the directory
    QFile pwmDir(pwmPath);
    if (!pwmDir.exists()) {
        qWarning() << "Failed to export PWM channel. Directory does not exist:" << pwmPath;
        return false;
    }

    return true;
}

bool PWM::setPeriod(int period) {
    QFile file(pwmPath + "period");
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream stream(&file);
        stream << period;
        file.close();
        return true;
    }
    qWarning() << "Failed to set PWM period";
    return false;
}

bool PWM::setDutyCycle(int dutyCycle) {
    QFile file(pwmPath + "duty_cycle");
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream stream(&file);
        stream << dutyCycle;
        file.close();
        return true;
    }
    qWarning() << "Failed to set PWM duty cycle";
    return false;
}

bool PWM::enable() {
    QFile file(pwmPath + "enable");
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream stream(&file);
        stream << 1;  // 1 表示启用
        file.close();
        return true;
    }
    qWarning() << "Failed to enable PWM";
    return false;
}

bool PWM::disable() {
    QFile file(pwmPath + "enable");
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream stream(&file);
        stream << 0;  // 0 表示禁用
        file.close();
        return true;
    }
    qWarning() << "Failed to disable PWM";
    return false;
}

九. 参考链接 

在Ubuntu中交叉编译Opencv 4.5.1 运行于Tina Linux中(整合帖) | 全志在线开发者论坛 (aw-ol.com)

全志D1-H,官方SDK Tina2.0 移植 QT5.15 | 全志在线开发者论坛

通过buildroot解决需要移植QT库的问题!!!!步骤详细,需要的拿走_buildroot 如何编译eglfs的库-CSDN博客

全志D1-H,官方SDK Tina2.0 移植 QT5.15 / 全志 SOC / WhyCan Forum(哇酷开发者社区)

给Buildroot(全志D1)移植RTL8723DS驱动 | 全志在线开发者论坛 (aw-ol.com)

【Sipeed D1 Dock Pro】QT初体验 | 全志在线开发者论坛 (aw-ol.com)

https://doc.embedfire.com/linux/imx6/base/zh/latest/submission/buildroot2017-1.html?highlight=buildroot

git clone出现 fatal: unable to access 'https://github.com/...'的解决办法(亲测有效)-CSDN博客

通过buildroot解决需要移植QT库的问题!!!!步骤详细,需要的拿走_buildroot 如何编译eglfs的库-CSDN博客

使用D1-H驱动树莓派DSI屏幕 | 全志在线开发者论坛 (aw-ol.com)

【极简操作】使用builroot 2021一键编译生成D1 nezha 系统镜像! | 全志在线开发者论坛 (aw-ol.com)

10【正点原子】I.MX6U_Buildroot构建Qt根文件系统V1.0.pdf

感谢全志官方友情赠送的 D1 RISC-V 开发板, 坐等release sdk / 全志 SOC / WhyCan Forum(哇酷开发者社区)

使用builroot 2021一键编译生成D1 nezha 系统镜像

使用D1-H驱动树莓派DSI屏幕 | 全志在线开发者论坛 (aw-ol.com)

【极简操作】使用builroot 2021一键编译生成D1 nezha 系统镜像! | 全志在线开发者论坛 (aw-ol.com)

Qt——Ubuntu下安装Qt Creator的方法步骤总结及其界面功能与基本设置简介(Qt简介、Qt Creator版本选择、软件入门、常用设置)_qtcreator-CSDN博客

【极简操作】使用builroot 2021一键编译生成D1 nezha 系统镜像! | 全志在线开发者论坛 (aw-ol.com)

Buildroot--如何配置安装带Qt5的文件系统_buildroot qt5-CSDN博客

能不能弄个qt交叉编译的教程,谢谢! / 全志 SOC / WhyCan Forum(哇酷开发者社区)

能不能弄个qt交叉编译的教程,谢谢! / 全志 SOC / WhyCan Forum(哇酷开发者社区)

一步一步分享在Windows QtCreator/Mingw平台编译运行调试LittleVGL / 计算机图形/GUI/RTOS/FileSystem/OpenGL/DirectX/SDL2 / WhyCan Forum(哇酷开发者社区)

Ubuntu 14.04 下安装Qt5 并配置开发环境_ubuntu14.04安装qt-CSDN博客

Ubuntu 14.04 下安装Qt5 并配置开发环境_ubuntu14.04安装qt-CSDN博客

QTCreator出现提示无法覆盖文件 /home/xxx/.config/QtProject/Qtcreator/qtversion.xml : Permission denied的错误 - 不明白就去明白 - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/888270.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI知识点】残差网络(ResNet,Residual Networks)

残差网络&#xff08;ResNet&#xff0c;Residual Networks&#xff09; 是由微软研究院的何凯明等人在 2015 年提出的一种深度神经网络架构&#xff0c;在深度学习领域取得了巨大的成功。它通过引入残差连接&#xff08;Residual Connection&#xff09; 解决了深层神经网络中…

Linux--IO模型与高级IO重要概念

什么是IO&#xff1f; IO是指计算机系统与外部世界进行数据交换的过程。在计算机中&#xff0c;IO通常用于与外部设备通信&#xff0c;这些设备包括键盘、鼠标、打印机、显示器、网络等。通过IO操作&#xff0c;计算机系统可以接收来自外部设备的输入数据&#xff0c;也可以将…

CSS圆角

在制作网页的过程中&#xff0c;有时我们可能需要实现圆角的效果&#xff0c;以前的做法是通过切图&#xff08;将设计稿切成便于制作成页面的图片&#xff09;&#xff0c;使用多个背景图像来实现圆角。在 CSS3 出现之后就不需要这么麻烦了&#xff0c;CSS3 中提供了一系列属性…

代数结构基础 - 离散数学系列(八)

目录 1. 群&#xff08;Group&#xff09; 群的定义 群的示例 2. 环&#xff08;Ring&#xff09; 环的定义 环的示例 3. 域&#xff08;Field&#xff09; 域的定义 域的示例 域在密码学中的应用 4. 实际应用场景 1. 对称性与加密 2. 误差检测与纠正 3. 数据编码…

jetlinks物联网平台学习5:dtu设备接入及温度报警场景联动

dtu设备接入及温度报警场景联动 1、平台端配置1、新建协议2、新建网络组件3、设备接入网关配置4、新增产品5、导入产品物模型6、新增设备7、场景联动配置7.1、触发规则7.2、触发条件7.3、执行动作 2、平台端验证场景联动 1、平台端配置 下载三个文件 https://hanta.yuque.com…

40条经典ChatGPT论文指令,圈定选题和进行论文构思

目录 1、用ChatGPT圈定选题范围2、用ChatGPT生成研究方法和思路3、用ChatGPT扩展论文观点和论证4、用ChatGPT辅助论文结构设计5、如何直接使用ChatGPT4o、o1、OpenAI Canvas6、OpenAI Canvas增强了啥&#xff1f;7、编程功能增强 &#x1f447; ChatGPT o1网页入口在文末&#…

如何让算法拥有“记忆”?一文读懂记忆化搜索

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 什么是记忆化搜索 二 相关题目练习 2.1 斐波那契数&#xff08;详解记忆化搜索&#xff09; ​编辑 解法一&#xff08;递归&#xff09;&#xff1a; 解法二&#xff08;记…

免费高可用软件

高可用软件是指那些能够提供高可用性、高可靠性的软件&#xff0c;它们在各种应用场景下都能确保系统的稳定运行。以下是四款免费的高可用软件&#xff0c;它们在不同领域都表现出色&#xff0c;能够满足各种高可用性需求。 一、PanguHA PanguHA是一款专为Windows平台设计的双…

【吊打面试官系列-MySQL面试题】试述视图的优点?

大家好&#xff0c;我是锋哥。今天分享关于【试述视图的优点&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 试述视图的优点&#xff1f; (1) 视图能够简化用户的操作 (2) 视图使用户能以多种角度看待同一数据&#xff1b; (3) 视图为数据库提供了一定程度的…

一、机器学习算法与实践_06迭代法和KMeans、线性回归、逻辑回归算法笔记

0 迭代法 迭代法不仅是机器学习、深度学习的核心&#xff0c;也是整个人工智能领域的重要概念&#xff0c;其对于算法的设计和实现至关重要 0.1 适合场景 对于不能一次搞定的问题&#xff0c;将其分成多步来解决&#xff0c;逐步逼近解决方案 0.2 典型应用 KMeans 聚类算法…

基于SpringBoot博物馆游客预约系统【附源码】

基于SpringBoot博物馆游客预约系统 效果如下&#xff1a; 主页面 注册界面 展品信息界面 论坛交流界面 后台登陆界面 后台主界面 参观预约界面 留言板界面 研究背景 随着现代社会的快速发展和人们生活水平的提高&#xff0c;文化生活需求也在日益增加。博物馆作为传承文化、…

关于CSS 案例_新闻内容展示

新闻要求 标题:居中加粗发布日期: 右对齐分割线: 提示, 可以使用 hr 标签正文/段落: 左侧缩进插图: 居中显示 展示效果 审核过不了&#xff0c;内容没填大家将就着看吧。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&qu…

Java | Leetcode Java题解之第461题汉明距离

题目&#xff1a; 题解&#xff1a; class Solution {public int hammingDistance(int x, int y) {int s x ^ y, ret 0;while (s ! 0) {s & s - 1;ret;}return ret;} }

新款平行进口奔驰GLS450升级原厂AR实景导航人机交互行车记录仪等功能

平行进口的24款奔驰GLS450升级原厂中规导航主机通常具备以下功能&#xff1a; 人机交互系统&#xff1a;该导航主机配备了人机交互系统&#xff0c;可以通过触摸屏、旋钮或语音控制等方式与导航系统进行交互&#xff0c;方便驾驶者进行导航设置和操作。 实景AR导航&#xff1…

基于springboot+小程序的智慧物业平台管理系统(物业1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 智慧物业平台管理系统按照操作主体分为管理员和用户。 1、管理员的功能包括报修管理、投诉管理管理、车位管理、车位订单管理、字典管理、房屋管理、公告管理、缴费管理、维修指派管理、…

03 Trace窗口3种过滤器

一、PreFilter &#xff1a;主要对报文进行过滤

EtherNet/IP 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherNet/IP 转 EtherNet/IP GW系列型号 MS-GW22 概述 简介 MS-GW22 是 EtherNet/IP 和 EtherNet/IP 协议转换网关&#xff0c;…

vue快速上手

文章目录 vue快速上手vue简述框架介绍mvvm vue使用1.npm2.vue cli1.打开 vue 官网2.快速上手3.切换目录到我们创建的应用位置&#xff0c;安装依赖3.运行vue项目 vue快速上手 vue简述 框架介绍 mvvm vue使用 1.npm 包管理器 安装nodejs就好了 2.vue cli 1.打开 vue 官网…

WebRTC Connection Negotiate解决

最近有个项目 &#xff0c;部署之后一直显示&#xff0c;查了一些资料还是没有解决&#xff0c;无奈只有自己研究解决&#xff1f; 什么是内网穿透&#xff1f; 我们访问我们自己的官网产品页面&#xff0c;我们的服务器是一个单独的个体&#xff0c;有独立的公网ip&#xf…

【C/C++】错题记录(四)

题目一 一个函数可以有很多个返回值&#xff08;有很多个return语句&#xff09;&#xff0c;但是最终只能有一个return语句执行。 题目二 题目三 题目四 题目五 程序数据结构算法 题目六 题目七 题目八 题目九 D选项是语句……