IAP固件升级进阶(Qt上位机)

news2024/11/27 2:23:58

前言

时隔近一年,再次接触IAP固件升级,这次修改了以前的一些bug,同时新增一些实用性的功能。

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com。

上位机界面

视频演示

当Up对iap固件升级的机制有了更深的理解后_哔哩哔哩_bilibili

固件升级指令

重要代码

1.通过拖拽实现文件读取

// MainWindow类的dragEnterEvent方法  
// 当鼠标拖动文件进入MainWindow的边界时,触发此方法  
void MainWindow::dragEnterEvent(QDragEnterEvent *event)  
{  
    // 检查拖动的数据是否包含URLs(即文件路径)  
    if(event->mimeData()->hasUrls())  
        // 如果包含URLs,则接受拖放操作  
        event->acceptProposedAction();  
    else  
        // 如果不包含URLs,则忽略拖放操作  
        event->ignore();  
}  
  
// MainWindow类的dropEvent方法  
// 当鼠标在MainWindow内释放拖动的文件时,触发此方法  
void MainWindow::dropEvent(QDropEvent *event)  
{  
    // 获取拖放事件中的MIME数据  
    const QMimeData *mimeData = event->mimeData();  
    // 检查MIME数据是否包含URLs  
    if(mimeData->hasUrls())  
    {  
        // 获取URLs列表  
        QList<QUrl> urlList = mimeData->urls();  
        // 获取第一个URL的本地文件路径  
        QString fileName = urlList.at(0).toLocalFile();  
  
        // 获取textEdit_fwUpdateFile控件中的文本  
        QString text = ui->textEdit_fwUpdateFile->toPlainText();  
  
        // 检查文件名是否为空,并且textEdit_fwUpdateFile中的文本为空  
        if((!fileName.isEmpty()) && (text.isEmpty()))  
        {  
            // 检查文件扩展名是否为.bin  
            if (fileName.endsWith(".bin"))  
            {  
                // 创建一个QFile对象,用于读取文件  
                QFile file(fileName);  
                // 创建一个QFileInfo对象,用于获取文件信息  
                QFileInfo fileInfo(fileName);  
  
                // 初始化固件更新相关变量  
                fwPackIndex = 0;  
                // 获取文件大小  
                fwFileLen = fileInfo.size();  
                // 计算固件包的数量(根据fwPackLength,但fwPackLength在此代码中未定义)  
                fwPackNum = fwFileLen/fwPackLength+1;  
  
                // 尝试以只读方式打开文件  
                if(!file.open(QIODevice::ReadOnly))  
                    // 如果文件打开失败,则返回不执行后续操作  
                    return;  
  
                // 读取文件全部内容  
                binRawData = file.readAll();  
  
                // 在lineEdit_fwUpdateFile控件中显示文件名  
                ui->lineEdit_fwUpdateFile->setText(fileName);  
                // 在textEdit_fwUpdateFile控件中追加文件的十六进制表示  
                ui->textEdit_fwUpdateFile->append(binRawData.toHex());  
  
                // 关闭文件  
                file.close();  
  
                // 启用开始固件更新按钮  
                ui->pushButton_startFwUpdate->setEnabled(true);  
                // 禁用停止固件更新按钮  
                ui->pushButton_stopFwUpdate->setEnabled(false);  
  
                // 初始化固件更新状态  
                fwUpdateState = fwInit;  
                // 重置固件包索引  
                fwPackIndex = 0;  
                // 重置超出标记和索引(这些变量在代码中没有明确的定义和用途)  
                fwExceedFlag = 0;  
                fwExceedIndex = 0;  
  
                // 重置进度条  
                ui->progressBar_upgrade->reset();  
            }  
            else  
            {  
                // 如果文件扩展名不是.bin,则显示警告消息  
                QMessageBox::warning(this, tr("错误"), tr("无法打开正确的文件!"));  
            }  
        }  
    }  
}

2.串口初始化自动识别并连接带CH340的串口

void MainWindow::serialPortInit()
{
    // 获取所有可用的串口信息,并存储在QList<QSerialPortInfo>中  
    QList<QSerialPortInfo> comList = QSerialPortInfo::availablePorts();  
    
    // 清空comboBox_chooseCom控件中的所有项  
    ui->comboBox_chooseCom->clear();  
    
    // 创建一个QMap,用于存储串口名(portName)到comboBox中索引的映射  
    QMap<QString, int> portNameToIndexMap;  
    
    // 遍历所有可用的串口信息  
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {  
        // 构建一个显示文本,由串口名和描述组成  
        QString displayText = info.portName() + ": " + info.description();  
    
        // 获取当前comboBox的项数,作为新项的索引  
        int index = ui->comboBox_chooseCom->count();  
    
        // 在comboBox_chooseCom控件中添加新的显示文本  
        ui->comboBox_chooseCom->addItem(displayText);  
    
        // 将当前串口名及其索引存入映射中  
        portNameToIndexMap.insert(info.portName(), index);  
    
        // 检查串口的描述或制造商信息是否包含"CH340"(不区分大小写)  
        if (info.description().contains("CH340", Qt::CaseInsensitive) ||  
            info.manufacturer().contains("CH340", Qt::CaseInsensitive)) {  
            // 使用映射来查找该串口名对应的索引  
            // 由于之前已经将串口名和索引存入映射,这里肯定能找到  
            if (portNameToIndexMap.contains(info.portName())) {  
                // 设置comboBox_chooseCom的当前项为找到的包含"CH340"的串口  
                ui->comboBox_chooseCom->setCurrentIndex(portNameToIndexMap.value(info.portName()));  
            }  
            // 找到后退出循环,因为我们只需要设置第一个匹配的串口  
            break;  
        }  
    }
}

3.IAP相关代码,跟上位机通信的逻辑部分

// IapRcvDataProc 函数用于处理接收到的IAP(In-Application Programming)数据消息  
void IapRcvDataProc(MSG_S stMsg)  
{  
    // 定义一个缓冲区用于存储接收到的消息数据  
    u8 MsgData[100];  
    // 将接收到的消息数据复制到MsgData缓冲区中  
    memcpy(MsgData, stMsg.szData, stMsg.u8Len);  
    u8 cmd = MsgData[__CmdIndex]; // 获取命令码  
  
    u8 i = 0; // 循环计数器  
  
    // 根据接收到的命令码的第3位(MsgData[2]的高4位)来构造回复命令的第1个字节  
    u8 replyCmd1 = 0xB0 | (MsgData[2] >> 4);  
  
    // 定义一个发送消息的缓冲区  
    u8 txMsg[20] = {0};  
  
    // 根据命令码执行不同的操作  
    switch(cmd)  
    {  
        case 0xF0: // 固件更新请求  
            // 读取固件更新标志位  
            SystemFlashRead(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  
            // 清除IAP结构体stIap的所有数据  
            memset(&stIap, 0, sizeof(IAP_S));  
            // 复制固件版本号到IAP结构体的flashData字段  
            memcpy(&stIap.stFwVer.flashData, &MsgData[6], 3);  
            // 将固件版本号写入到固件版本地址  
            SystemFlashWrite(__FwVersionAddr, __FwUpdateFlagSize, &stIap.stFwVer.flashData);  
            // 构造回复消息  
            txMsg[0] = replyCmd1;  
            txMsg[1] = cmd;  
            txMsg[2] = 1; // 表示成功的标志位  
            // 发送回复消息  
            UartLoadTxMsg(1, txMsg, 3);  
            break;  
  
        case 0xF1: // 固件擦除 
            // 设置固件标志位为无固件  
            stIap.u32FwFlag = __NO_FW;
            // 写入固件更新标志位  
            SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  
            // 擦除应用程序空间  
            EraseFwSpace(__APP_START_ADDR, __APP_SIZE / __FLASH_PAGE_SIZE);  
            break;  
  
        case 0xF2: // 固件数据接收  
            // 更新帧索引  
            stIap.u16FwFrameIndex++;  
            // 设置接收帧的长度  
            stIap.stRcvFrame.u8Length = MsgData[6];  
            // 复制接收到的数据到IAP结构体的接收帧缓冲区  
            memcpy(&stIap.stRcvFrame.u8Data, &MsgData[7], stIap.stRcvFrame.u8Length);  
            // 逐块(每块4个字节)将数据写入到应用程序起始地址  
            for(i = 0; i < stIap.stRcvFrame.u8Length; i += 4)  
            {  
                FlashWriteWord(__APP_START_ADDR + stIap.u32WriteAddrIndex, *(u32 *)&stIap.stRcvFrame.u8Data[i]);  
                stIap.u32WriteAddrIndex += 4; // 更新写入地址  
            }  
            // 构造回复消息,包含帧编号和成功标志位  
            txMsg[0] = replyCmd1;  
            txMsg[1] = cmd;  
            txMsg[2] = MsgData[4]; // 帧编号的高字节  
            txMsg[3] = MsgData[5]; // 帧编号的低字节  
            txMsg[4] = 1; // 成功标志位  
            // 发送回复消息  
            UartLoadTxMsg(1, txMsg, 5);  
            break;  
  
        case 0xF3: // 固件更新完成  
            // 设置固件标志位为已有固件  
            stIap.u32FwFlag = __HAVE_FW_REPLY;  
            // 将固件标志位写入到固件更新标志地址  
            SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  
            // 跳转到应用程序开始执行(切换到新的固件)  
            JumpToApplication();  
            break;  
  
        case 0xF4: // 固件版本查询  
            // 从固件版本地址读取固件版本数据  
            SystemFlashRead(__FwVersionAddr, __FwUpdateFlagSize, &stIap.stFwVer.flashData);  
            // 如果读取到的固件版本数据不是0xFFFFFFFF(表示无固件或无效版本)  
            if (stIap.stFwVer.flashData != 0xFFFFFFFF)   
            {  
                // 解析固件版本数据到结构体中的major、minor、patch字段  
                stIap.stFwVer.major =  stIap.stFwVer.flashData & 0xFF; // 低8位为major版本  
                stIap.stFwVer.minor =  (stIap.stFwVer.flashData >> 8) & 0xFF; // 接下来的8位为minor版本  
                stIap.stFwVer.patch =  (stIap.stFwVer.flashData >> 16) & 0xFF; // 再接下来的8位为patch版本  
            }  
            else  
            {  
                // 如果固件版本数据为0xFFFFFFFF,则清空固件版本结构体  
                memset(&stIap.stFwVer, 0, sizeof(stIap.stFwVer));  
            }  
            // 构造回复消息,包含固件版本号  
            txMsg[0] = replyCmd1;  
            txMsg[1] = cmd;  
            txMsg[2] = stIap.stFwVer.major; // 固件major版本  
            txMsg[3] = stIap.stFwVer.minor; // 固件minor版本  
            txMsg[4] = stIap.stFwVer.patch; // 固件patch版本  
            // 发送固件版本回复消息  
            UartLoadTxMsg(1, txMsg, 5);  
            break;
		}			
}

 4.APP检测将要更新的固件版本是否高于当前固件版本

// 从系统闪存中读取固件版本信息  
SystemFlashRead(__FwVersionAddr, __FwUpdateFlagSize, &stApp.stFwVer.flashData);  
  
// 检查读取到的固件版本数据是否不是0xFFFFFFFF(通常表示无效或未设置的值)  
if (stApp.stFwVer.flashData != 0xFFFFFFFF)   
{  
	// 如果不是无效值,则解析固件版本数据  
	// 提取主要版本号(低8位)  
	stApp.stFwVer.major = stApp.stFwVer.flashData & 0xFF;  
	// 提取次要版本号(接下来的8位)  
	stApp.stFwVer.minor = (stApp.stFwVer.flashData >> 8) & 0xFF;  
	// 提取补丁版本号(再接下来的8位,但注意这里实际上只用了24位来表示版本)  
	stApp.stFwVer.patch = (stApp.stFwVer.flashData >> 16) & 0xFF;  
}  
else  
{  
	// 如果固件版本数据是无效值,则清空固件版本结构体  
	memset(&stApp.stFwVer, 0, sizeof(stApp.stFwVer));  
}  
  
// 从接收到的消息数据中复制固件版本数据(MsgData是一个包含固件版本数据的数组)  
memcpy(&stRxFwVer.flashData, &MsgData[6], 3);  
  
// 解析接收到的固件版本数据  
// 提取主要版本号  
stRxFwVer.major = stRxFwVer.flashData & 0xFF;  
// 提取次要版本号  
stRxFwVer.minor = (stRxFwVer.flashData >> 8) & 0xFF;  
// 提取补丁版本号  
stRxFwVer.patch = (stRxFwVer.flashData >> 16) & 0xFF;  
  
// 调用函数比较当前固件版本(stApp.stFwVer)和接收到的固件版本(stRxFwVer)  
// 如果接收到的版本更高或等于当前版本,则返回1  
tmp = isNewVersionHigherOrEqual(&stApp.stFwVer, &stRxFwVer);  
  
// 根据比较结果决定下一步操作  
if (tmp == 1)  
{  
	// 如果接收到的版本更高或等于当前版本  
	// 设置固件标志位为需要固件更新  
	stApp.u32FwFlag = __HAVE_FW_UPDATE;  
	// 将固件标志位写入到固件更新标志地址  
	SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stApp.u32FwFlag);  
	// 创建一个任务来跳转到IAP模式进行固件更新(假设Task2和JumpToIap是任务相关的函数和参数)  
	OS_TaskCreat(Task2, JumpToIap, 100);  
	// 注意:这里可能还需要发送一个确认消息给发送方,但代码中没有体现  
}  
else  
{  
	// 构造回复消息  
	txMsg[0] = replyCmd1; // 回复命令标识符  
	txMsg[1] = cmd;       // 原始命令  
	// 如果接收到的版本不是更高,则在回复消息中添加0表示无需更新  
	txMsg[2] = 0;  
	// 发送无需更新的确认消息  
	UartLoadTxMsg(1, txMsg, 3);  
} 

 5.上位机对收到命令进行处理

void MainWindow::rcvFwReply(QByteArray *protocalData)
{
    // 定义一个字节数组pRxData来存储接收到的数据
    uint8_t pRxData[100];
    // 定义一个QString对象来存储接收到的固件版本号信息(但在此段代码中未使用)
    QString rcvFwVer;

    // 将传入的QByteArray中的数据复制到pRxData数组中
    memcpy(pRxData, protocalData->data(), protocalData->size());

    // 清空并设置文本编辑框的换行模式
    ui->textEdit_fwRcvInfo->clear();
    ui->textEdit_fwRcvInfo->setWordWrapMode(QTextOption::WordWrap);

    // 根据接收到的数据的第四个字节进行不同的处理
    switch (pRxData[3])
    {
        case 0xF0:
            if(pRxData[4] == 1)
            {
                // 重置fwExceedIndex和fwUpdateState变量,并启动相关的定时器
                    fwExceedIndex = 0;
                fwUpdateState = fwTransfer;
                fwNumCmd(); // 调用fwNumCmd函数(该函数在此段代码中未给出)
                    fwUpdateTimer->start(1000); // 定时器每隔1000毫秒(1秒)触发一次
                    fwUpdateTimeOut->start(2000); // 另一个定时器每隔2000毫秒(2秒)触发一次
                    // 在文本编辑框中插入“开始升级”文本
                    ui->textEdit_fwRcvInfo->insertPlainText(QString("开始升级"));
                    // 这里fwExceedIndex被重复设置为了0,可能是冗余代码
                    fwExceedIndex = 0;
            }
            else if (pRxData[4] == 0)
            {
            // 在文本编辑框中插入“固件已是最新版/对方拒绝升级”文本
                ui->textEdit_fwRcvInfo->insertPlainText(QString("固件已是最新版/对方拒绝升级"));
            }
            break;

        case 0xF2:
            // 从接收到的数据的第五个和第六个字节中解析出一个uint16_t类型的索引值
            uint16_t u16FwPackIndex;
            *(uint16_t *)&u16FwPackIndex = *(uint16_t *)&pRxData[4];

            // 如果解析出的索引值与当前fwPackIndex相等
            if(u16FwPackIndex == fwPackIndex)
            {
                // 如果接收到的数据的第七个字节是1
                if(pRxData[6] == 1)
                {
                    // 重置fwExceedIndex变量
                    fwExceedIndex = 0;

                    // 在文本编辑框中插入“第 [索引值] 帧传输成功”的文本
                    ui->textEdit_fwRcvInfo->insertPlainText(QString("第 ["));
                    ui->textEdit_fwRcvInfo->insertPlainText(QString::number(fwPackIndex));
                    ui->textEdit_fwRcvInfo->insertPlainText(QString("]  帧传输成功"));

                    // 重置fwExceedIndex变量(这里再次被重复设置,可能是冗余代码)
                    fwExceedIndex = 0;

                    // 启动相关的定时器
                    fwUpdateTimer->start(20); // 定时器每隔20毫秒触发一次
                    fwUpdateTimeOut->start(2000); // 另一个定时器每隔2000毫秒触发一次

                    // 更新进度条的值
                    ui->progressBar_upgrade->setValue(static_cast<int>(fwPackIndex * 100.0 / fwPackNum));
                }
                else if (pRxData[6] == 0)
                {
                    // 在文本编辑框中插入“[索引值] 帧传输失败”的文本
                    ui->textEdit_fwRcvInfo->insertPlainText(QString("["));
                    ui->textEdit_fwRcvInfo->insertPlainText(QString::number(fwPackIndex));
                    ui->textEdit_fwRcvInfo->insertPlainText(QString("] 帧传输失败"));
                }
            }
            break;

        case 0xF3:
            if(pRxData[4] == 1)
            {
                // 更新状态为升级完成
                fwUpdateState = fwComplete;
                // 在文本编辑框中插入“对方回复升级成功”的文本
                ui->textEdit_fwRcvInfo->insertPlainText(QString("对方回复升级成功"));
                // 重置一些变量
                fwExceedIndex = 0;
                fwUpdateTimeOut->stop();
            }
            else if (pRxData[4] == 0)
            {
                // 在文本编辑框中插入“对方回复升级失败”的文本
                ui->textEdit_fwRcvInfo->insertPlainText(QString("对方回复升级失败"));
            }
            // 无论成功还是失败,都重置UI中的按钮状态并更新fwUpdateState和其他相关变量
            ui->pushButton_startFwUpdate->setEnabled(true);
            ui->pushButton_stopFwUpdate->setEnabled(false);
            fwUpdateState = fwInit;
            fwPackIndex = 0;
            fwExceedFlag = 0;
            fwExceedIndex = 0;
            break;

        case 0xF4:
            // 从接收到的数据的第五、六、七个字节中分别获取固件的主版本号、次版本号和补丁版本号
            quint8 data1 = static_cast<quint8>(pRxData[4]);
            quint8 data2 = static_cast<quint8>(pRxData[5]);
            quint8 data3 = static_cast<quint8>(pRxData[6]);

            // 将十六进制转换为十进制
            int versionMajor = data1;
            int versionMinor = data2;
            int versionPatch = data3;

            // 组合成版本号字符串,并确保补丁版本号至少为两位数,不足补0
            QString versionString = QString("对方固件版本号为:%1.%2.%3").arg(versionMajor).arg(versionMinor).arg(versionPatch, 2, 10, QChar('0')).toUpper();

            // 在文本编辑框中插入版本号字符串
            ui->textEdit_fwRcvInfo->insertPlainText(versionString);
            break;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读笔记】PA-SAM: Prompt Adapter SAM for High-Quality Image Segmentation

1.论文介绍 PA-SAM: Prompt Adapter SAM for High-Quality Image Segmentation PA-SAM&#xff1a;用于高质量图像分割的提示适配器SAM 2024年 ICME Paper Code 2.摘要 Segment Anything Model&#xff0c;SAM在各种图像分割任务中表现出了优异的性能。尽管SAM接受了超过10亿…

【Unity每日一记】如何在Unity里面添加视频,做媒体屏幕

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

降噪领夹麦克风哪个牌子好?揭秘无线领夹麦克风哪个降噪好

相信很多新手视频创作者都有一个疑问&#xff1a;为什么别人的视频或者直播音质这么清晰&#xff0c;几乎没什么噪音呢&#xff1f;其实最主要的就是麦克风的原因&#xff0c;相机或手机内置的麦克风是无法提供高质量的音频记录以及很好的指向性的。 想要拍摄出来的视频作品拥有…

零基础直接上手java跨平台桌面程序,使用javafx(七)用户操作界面探讨,这个系列结束

GUI&#xff0c;我们还是喜欢web。如果javafx有像wpf的WebView2差不多的功能&#xff0c;我们就开心了scene builder中拖出一个webview&#xff0c;然后再回到代码中。发现<?import javafx.scene.web.*?>是红色的&#xff0c;我们缺少配置。于是在pom.xml中添加JavaFX依…

5.华为交换机局域网vlan网段隔离配置

目的:PC1与PC2互通 与PC3隔离 LSW1配置 [Huawei]vlan batch 10 20 [Huawei]int g0/0/1 [Huawei-GigabitEthernet0/0/1]port link-type access [Huawei-GigabitEthernet0/0/1]port default vlan 10 [Huawei-GigabitEthernet0/0/1]int g0/0/2 [Huawei-GigabitEthernet0/0/2]port…

【网络安全学习】使用Kali做渗透情报收集-02-<指纹识别+目录扫描>

1.指纹识别 指纹识别是指通过一些特征或特定文件来识别目标网站或系统的类型、版本、组件等信息&#xff0c;以便寻找相应的漏洞或攻击方法。 主动指纹识别 通过向目标系统发送正常和异常的请求以及对文件内容的查找&#xff0c;记录响应方式&#xff0c;然后与指纹库进行对比…

树莓派pico入坑笔记,mpy文件制作

py文件本质上就是一个文本文件&#xff0c;运行时使用python解释器解释成机器运行的代码&#xff0c;然而对于单片机这样资源紧张的计算机来说&#xff0c;一个库文件或者运行代码文件的文本还是太大了。 因此&#xff0c;为了减小代码存储空间&#xff0c;可以将代码预处理&a…

Java多线程下载工具,多线程,多任务,断点续传,GUI

目录 一、题目要求 二、效果展示 三、功能实现 四、代码 一、题目要求 序号 功能名称 功能需求标识 简要描述 1 下载功能 Download 当用户输入一个下载链接后&#xff0c;能识别链接并开始多线程下载工作&#xff0c;包括线程监听、线程管理等。 2 续传功能 …

【Effective Web】常见的css居中方式

CSS居中方式 水平居中 text-align:center 适用范围&#xff1a;容器中都是行内元素 缺点&#xff1a;容器内所有元素都会居中&#xff0c;如果是文本描述需要左对齐&#xff0c;需要增加text-align:left覆盖 margin: 0 auto 适用范围&#xff1a;容器宽度固定。子元素宽度…

多目标粒子群算法(MOPSO):原理讲解与代码实现 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 粒子群算法 多目标粒子群算法 一、外部档案…

嵌入式linux系统中SPI子系统原理分析01

大家好,今天给大家分享一下,如何使用linux系统中的SPI通信协议,实现主从设备之间的信息传递。 SPI是一种常见的设备通用通信协议。它是一个独特优势就是可以无中断发送数据,可以连续发送或接收任意数量的位。而在I2C和UART中,数据以数据包的形式发送,有限定位数。 …

【Java05】Java中的多维数组

从数组底层运行机制上看&#xff0c;Java没有多维数组一说。所谓多维数组&#xff0c;是说一个引用变量指向的元素也是引用变量。 例如&#xff0c;type[] arrayName是个指向type类型元素的数组。倘若type也是数组引用变量&#xff0c;比如int[]&#xff0c;那么这个数组就可以…

算法题解记录29+++全排列(百日筑基)

一、题目描述 题目难度&#xff1a;中等 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示…

VUE3版本新特性

VUE3版本新特性 VUE3和VUE2的区别路由的使用vite安装项目新特性使用 1.VUE3和VUE2的区别 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece 于 2022 年 2 月 7 日星期一成为新的默认版本! Vue3性能更高,初次渲染快55%, 更新渲染快133% 。…

ROS中使用超声波传感器(附代码)

在ROS中使用超声波传感器通常涉及到订阅或发布sensor_msgs/Range类型的消息。下面是一个简单的示例&#xff0c;展示了如何使用C在ROS中编写一个超声波传感器的驱动程序。这个例子假设你有一个超声波传感器连接到了Arduino或者其他微控制器&#xff0c;并且该微控制器已经通过串…

FreeRtos-09事件组的使用

1. 事件组的理论讲解 事件组:就是通过一个整数的bit位来代表一个事件,几个事件的or和and的结果是输出 #define configUSE_16_BIT_TICKS 0 //configUSE_16_BIT_TICKS用1表示16位,用0表示32位 1.1 事件组适用于哪些场景 某个事件若干个事件中的某个事件若干个事件中的所有事…

经历的分享

我是三本计算机科学技术跨考上岸的学生&#xff0c;本科阶段技术能力并没有掌握多少&#xff0c;在选择导师时屡屡碰壁&#xff0c;我当时向许多计算机方向的导师&#xff0c;比如大数据方向,计算机视觉 迁移学习和图像处理方向的导师全都拒绝了我&#xff0c;最终学校给我分配…

Ubuntu 在线或离线安装docker

查看自己的ubuntu版本 在终端中执行以下命令&#xff1a; lsb_release -a 终端中的复制粘贴&#xff1a; ctrl shift c ctrl shifr v 在线安装docker&#xff08;不需要外网&#xff09;: 命令行安装&#xff1a;Ubuntu Docker -- 从入门到实践 看完…

Apollo9.0 PNC源码学习之Control模块(五)—— 基于LQR的横向控制

前面文章&#xff1a; Apollo9.0 PNC源码学习之Control模块&#xff08;一&#xff09; Apollo9.0 PNC源码学习之Control模块&#xff08;二&#xff09; Apollo9.0 PNC源码学习之Control模块&#xff08;三&#xff09; Apollo9.0 PNC源码学习之Control模块&#xff08;四&…

git Fork或者git clone克隆别人的项目到自己的仓库如何保持原仓库同步

一、问题描述 有时候我们会clone别人的项目到自己的仓库来进行二次开发,开发好之后提交到自己的仓库&#xff0c;如有原仓库有更新了,可以选择性的进行同步 二、解决方法 这里以ruoyi-vue-pro得前端项目来进行演示&#xff0c;创建一个目录&#xff0c;在目录下随便创建一个文…