沁恒蓝牙芯片CH582:蓝牙OTA升级技术详解与应用探索

文章目录

  • 一、前言
    • 1.WCH 蓝牙空中升级(BLE OTA)概述
    • 2. WCH BLE SDK DFU 工作原理(方式一)
  • 二、移植程序
    • 1.找到BackUpgrade_OTA例程
    • 2.添加文件到工程
      • 2.1 添加文件
      • 2.2 如何添加
    • 3.修改APP工程
      • 3.1 修改peripheral_main.c文件
      • 3.2 修改peripheral.c文件
    • 4.修改Link.Ld文件
  • 三、OTA升级
    • 1.合并文件
    • 2.下载bin文件
    • 3.APP升级程序
      • 3.1 OTA升级工具
      • 3.2 搜索蓝牙,连接
      • 3.3 选择升级的固件
      • 3.4 升级过程
      • 3.5 升级完成
      • 3.6 升级结果
  • 四、总结


一、前言

1.WCH 蓝牙空中升级(BLE OTA)概述

DFU(Device Firmware Update)设备固件升级,OTA(Over The Air)是实现 DFU 的一种方式,OTA 的全称应该是 OTA DFU,即通过空中无线方式实现设备固件升级。OTA 也可以称为 FOTA,即 Firmware OTA。WCH BLE 系列芯片除了可以通过无线方式(OTA)-方式一/二 DFU 进行升级,也可以通过有线方式-方式三 DFU 进行升级,比如通过内置 BOOT(UART/USB)来升级设备固件。

不管采用 OTA 方式还是有线通信方式,DFU 包括后台式(background)和非后台式两种模式。后台式 DFU-类似方式一 DFU,又称静默式 DFU(Silent DFU),即新固件下载属于应用程序功能的一部分,在新固件下载过程中,应用可以正常使用,下载完成后,系统完成执行新固件的操作,至此整个升级过程结束(比如EVT/BLE/BackupUpgrade_XXX 例子)。非后台式 DFU-方式二 DFU,在升级的时候整个升级过程中 APP 正常功能都无法使用。系统需要先从应用模式跳入到 BootLoader/IAP 模式,由 BootLoader/IAP 进行新固件下载工作,直接覆盖老固件,至升级结束
(比如EVT\EXAM\BLE\OnlyUpdateApp_XXX 例子)。

在这里插入图片描述

2. WCH BLE SDK DFU 工作原理(方式一)

方式一上电优先运行 IAP 程序,之后跳转 APP 执行用户程序。APP 文件为独立完整的功能文件,运行时可以通过无线方式接收完整的 OTA 备份升级文件,然后软复位进入 IAP 程序,IAP 会将备份区的 OTA 升级文件拷贝到 APP应用程序区,最后跳转回升级后的 APP 应用程序运行新的固件。升级固件带 LIB 编译(LIB 约 90K)。

注意:备份升级方式,适用于 flash 资源充裕的应用,优点是 APP+LIB 都可以升级,缺点是 flash 需要对半分使用。

方式一 DFU 包含 3 个文件 JumpIAP + APP + IAP(APP 是用户功能代码,也是实现产品功能和升级功能的主体,对应 BackupUpgrade_OTA 产生的 hex 文件;IAP 是编程升级 APP 的代码,对应 BackupUpgrade_IAP 产生的 hex 文件;JumpIAP 为跳转 IAP 代码,
对应BackupUpgrade_JumpIAP 产生的 hex 文件)。
在这里插入图片描述

选择对应的文件,点击合并,产生一个 BIN 文件,然后使用 ISP 工具下载即可。


二、移植程序

1.找到BackUpgrade_OTA例程

根据上面原理的介绍,如果我们要在我们的APP程序上进行OTA升级,我们需要根据BackUpgrade_OTA例程来修改我们的程序。

BackUpgrade_OTA例程在如下目录里。

  1. 在这里插入图片描述
  2. 在这里插入图片描述
  3. 在这里插入图片描述

2.添加文件到工程

2.1 添加文件

添加ota.h,OTAprogile.c和OTAprogile.h文件到自己的APP工程中
在这里插入图片描述

2.2 如何添加

在需要添加的位置右键添加,选择已存在文件。
在这里插入图片描述

3.修改APP工程

3.1 修改peripheral_main.c文件

添加头文件

#include "OTA.h"
#include "OTAprofile.h"

添加代码

/* 记录当前的Image */
unsigned char CurrImageFlag = 0xff;

/* 用于APP判断文件有效性 */
const uint32_t Address = 0xFFFFFFFF;

__attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address;

/* 注意:关于程序升级后flash的操作必须先执行,不开启任何中断,防止操作中断和失败 */
/*********************************************************************
 * @fn      ReadImageFlag
 *
 * @brief   读取当前的程序的Image标志,DataFlash如果为空,就默认是ImageA
 *
 * @return  none
 */
void ReadImageFlag(void)
{
    OTADataFlashInfo_t p_image_flash;

    EEPROM_READ(OTA_DATAFLASH_ADD, &p_image_flash, 4);
    CurrImageFlag = p_image_flash.ImageFlag;

    /* 程序第一次执行,或者没有更新过,以后更新后在擦除DataFlash */
    if((CurrImageFlag != IMAGE_A_FLAG) && (CurrImageFlag != IMAGE_B_FLAG))
    {
        CurrImageFlag = IMAGE_A_FLAG;
    }
}

//在main主函数里面添加以下函数
int main(void)
{
    ReadImageFlag();
}

3.2 修改peripheral.c文件

添加头文件

#include "OTA.h"
#include "OTAprofile.h"

添加代码

  1. 蓝牙参数
//一些蓝牙相关参数,这里就不一一介绍了
// How often to perform periodic event   多长时间执行一次周期事件
#define SBP_PERIODIC_EVT_PERIOD              1000//1600

// What is the advertising interval when device is discoverable (units of 625us, 80=50ms) 设备被发现时的广告间隔是多少(单位为625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL         80

// Limited discoverable mode advertises for 30.72s, and then stops  限制可发现模式的广告时间为30.72秒,然后停止
// General discoverable mode advertises indefinitely   一般可发现模式无限广告
#define DEFAULT_DISCOVERABLE_MODE            GAP_ADTYPE_FLAGS_GENERAL

// Minimum connection interval (units of 1.25ms, 6=7.5ms)最小连接间隔(以1.25ms为单位,6=7.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL    12

// Maximum connection interval (units of 1.25ms, 100=125ms)最大连接间隔(以1.25ms为单位,100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL    100//100

// How often to perform read rssi event  多久执行一次读取rssi事件
#define SBP_READ_RSSI_EVT_PERIOD             32//3200

 Parameter update delay  参数更新延迟
//#define SBP_PARAM_UPDATE_DELAY               6400
//
 PHY update delay  PHY更新延迟
//#define SBP_PHY_UPDATE_DELAY                 2400

// Slave latency to use parameter update   从延迟使用参数更新
#define DEFAULT_DESIRED_SLAVE_LATENCY        5

// Supervision timeout value (units of 10ms, 100=1s)  监督超时值(单位10ms, 100=1s)
#define DEFAULT_DESIRED_CONN_TIMEOUT         1000//100

// Whether to enable automatic parameter update request when a connection is formed
#define DEFAULT_ENABLE_UPDATE_REQUEST        TRUE

// Company Identifier: WCH
#define WCH_COMPANY_ID                       0x07D7

#define INVALID_CONNHANDLE                   0xFFFF
  1. OTA相关参数和函数
// OTA IAP VARIABLES
/* OTA通讯的帧 */
OTA_IAP_CMD_t iap_rec_data;

/* OTA解析结果 */
uint32_t OpParaDataLen = 0;
uint32_t OpAdd = 0;

/* flash的数据临时存储 */
__attribute__((aligned(8))) uint8_t block_buf[16];

/* Image跳转函数地址定义 */
typedef int (*pImageTaskFn)(void);
pImageTaskFn user_image_tasks;

/* Flash 擦除过程 */
uint32_t EraseAdd = 0;      // 擦除地址
uint32_t EraseBlockNum = 0; // 需要擦除的块数
uint32_t EraseBlockCnt = 0; // 擦除的块计数

/* FLASH 校验过程 */
uint8_t VerifyStatus = 0;

void OTA_IAPReadDataComplete(unsigned char index);
void OTA_IAPWriteData(unsigned char index, unsigned char *p_data, unsigned char w_len);
void Rec_OTA_IAP_DataDeal(void);
void OTA_IAP_SendCMDDealSta(uint8_t deal_status);

// Simple GATT Profile Callbacks
static OTAProfileCBs_t Peripheral_OTA_IAPProfileCBs = {
   OTA_IAPReadDataComplete, // Charactersitic value change callback
   OTA_IAPWriteData};
  1. 在void Peripheral_Init()函数中修改如下
// GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);

OTAProfile_AddService(GATT_ALL_SERVICES);//OTA Profile初始化

OTAProfile_RegisterAppCBs(&Peripheral_OTA_IAPProfileCBs);//OTA Profile读写回调函数注册
  1. 在uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)函数中添加如下代码
   if (events & SBP_PARAM_UPDATE_EVT2)
   {
     // Send connect param update request发送连接参数更新请求
     GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
                                          DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                          20,
                                          0,
                                          DEFAULT_DESIRED_CONN_TIMEOUT,
                                          Peripheral_TaskID);

     return (events ^ SBP_PARAM_UPDATE_EVT2);
   }
   if (events & OTA_FLASH_ERASE_EVT)
   {
     uint8_t status;

     PRINT("ERASE:%08x num:%d\r\n", (int)(EraseAdd + EraseBlockCnt * FLASH_BLOCK_SIZE), (int)EraseBlockCnt);
     status = FLASH_ROM_ERASE(EraseAdd + EraseBlockCnt * FLASH_BLOCK_SIZE, FLASH_BLOCK_SIZE);
     /* 擦除失败 */
     if (status != SUCCESS)
     {
       OTA_IAP_SendCMDDealSta(status);
       return (events ^ OTA_FLASH_ERASE_EVT);
     }

     EraseBlockCnt++;

     /* 擦除结束 */
     if (EraseBlockCnt >= EraseBlockNum)
     {
       PRINT("ERASE Complete\r\n");
       OTA_IAP_SendCMDDealSta(status);

       return (events ^ OTA_FLASH_ERASE_EVT);
     }
     return (events);
   }
  1. 在Peripheral.h文件中新增定义如下
#define OTA_FLASH_ERASE_EVT     0x0040  //OTA Flash
#define SBP_PARAM_UPDATE_EVT2   0x0080
  1. 在Peripheral.c文件中新增如下函数
/*********************************************************************
 * @fn      OTA_IAP_SendData
 *
 * @brief   OTA IAP发送数据,使用时限制20字节以内
 *
 * @param   p_send_data - 发送数据的指针
 * @param   send_len    - 发送数据的长度
 *
 * @return  none
 */
void OTA_IAP_SendData(uint8_t *p_send_data, uint8_t send_len)
{
  OTAProfile_SendData(OTAPROFILE_CHAR, p_send_data, send_len);
}

/*********************************************************************
 * @fn      OTA_IAP_SendCMDDealSta
 *
 * @brief   OTA IAP执行的状态返回
 *
 * @param   deal_status - 返回的状态
 *
 * @return  none
 */
void OTA_IAP_SendCMDDealSta(uint8_t deal_status)
{
  uint8_t send_buf[2];

  send_buf[0] = deal_status;
  send_buf[1] = 0;
  OTA_IAP_SendData(send_buf, 2);
}

/*********************************************************************
 * @fn      OTA_IAP_CMDErrDeal
 *
 * @brief   OTA IAP异常命令码处理
 *
 * @return  none
 */
void OTA_IAP_CMDErrDeal(void)
{
  OTA_IAP_SendCMDDealSta(0xfe);
}

/*********************************************************************
 * @fn      SwitchImageFlag
 *
 * @brief   切换dataflash里的ImageFlag
 *
 * @param   new_flag    - 切换的ImageFlag
 *
 * @return  none
 */
void SwitchImageFlag(uint8_t new_flag)
{
  uint16_t i;
  uint32_t ver_flag;

  /* 读取第一块 */
  EEPROM_READ(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);

  /* 擦除第一块 */
  EEPROM_ERASE(OTA_DATAFLASH_ADD, EEPROM_PAGE_SIZE);

  /* 更新Image信息 */
  block_buf[0] = new_flag;

  /* 编程DataFlash */
  EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
}

/*********************************************************************
 * @fn      DisableAllIRQ
 *
 * @brief   关闭所有的中断
 *
 * @return  none
 */
void DisableAllIRQ(void)
{
  SYS_DisableAllIrq(NULL);
}

/*********************************************************************
 * @fn      Rec_OTA_IAP_DataDeal
 *
 * @brief   接收到OTA数据包处理
 *
 * @return  none
 */
void Rec_OTA_IAP_DataDeal(void)
{
  switch (iap_rec_data.other.buf[0])
  {
  /* 编程 */
  case CMD_IAP_PROM:
  {
    uint32_t i;
    uint8_t status;

    OpParaDataLen = iap_rec_data.program.len;
    OpAdd = (uint32_t)(iap_rec_data.program.addr[0]);
    OpAdd |= ((uint32_t)(iap_rec_data.program.addr[1]) << 8);
    OpAdd = OpAdd * 16;

    OpAdd += IMAGE_A_SIZE;

    PRINT("IAP_PROM: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);

    /* 当前是ImageA,直接编程 */
    status = FLASH_ROM_WRITE(OpAdd, iap_rec_data.program.buf, (uint16_t)OpParaDataLen);
    if (status)
      PRINT("IAP_PROM err \r\n");
    OTA_IAP_SendCMDDealSta(status);
    break;
  }
  /* 擦除 -- 蓝牙擦除由主机控制 */
  case CMD_IAP_ERASE:
  {
    OpAdd = (uint32_t)(iap_rec_data.erase.addr[0]);
    OpAdd |= ((uint32_t)(iap_rec_data.erase.addr[1]) << 8);
    OpAdd = OpAdd * 16;

    OpAdd += IMAGE_A_SIZE;

    EraseBlockNum = (uint32_t)(iap_rec_data.erase.block_num[0]);
    EraseBlockNum |= ((uint32_t)(iap_rec_data.erase.block_num[1]) << 8);
    EraseAdd = OpAdd;
    EraseBlockCnt = 0;

    /* 检验就放在擦除里清0 */
    VerifyStatus = 0;

    PRINT("IAP_ERASE start:%08x num:%d\r\n", (int)OpAdd, (int)EraseBlockNum);

    if (EraseAdd < IMAGE_B_START_ADD || (EraseAdd + (EraseBlockNum - 1) * FLASH_BLOCK_SIZE) > IMAGE_IAP_START_ADD)
    {
      OTA_IAP_SendCMDDealSta(0xFF);
    }
    else
    {
      /* 启动擦除 */
      tmos_set_event(Peripheral_TaskID, OTA_FLASH_ERASE_EVT);
    }
    break;
  }
  /* 校验 */
  case CMD_IAP_VERIFY:
  {
    uint32_t i;
    uint8_t status = 0;

    OpParaDataLen = iap_rec_data.verify.len;

    OpAdd = (uint32_t)(iap_rec_data.verify.addr[0]);
    OpAdd |= ((uint32_t)(iap_rec_data.verify.addr[1]) << 8);
    OpAdd = OpAdd * 16;
    OpAdd += IMAGE_A_SIZE;
    PRINT("IAP_VERIFY: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);

    /* 当前是ImageA,直接读取ImageB校验 */
    status = FLASH_ROM_VERIFY(OpAdd, iap_rec_data.verify.buf, OpParaDataLen);
    if (status)
    {
      PRINT("IAP_VERIFY err \r\n");
    }
    VerifyStatus |= status;
    OTA_IAP_SendCMDDealSta(VerifyStatus);
    break;
  }
  /* 编程结束 */
  case CMD_IAP_END:
  {
    PRINT("IAP_END \r\n");

    /* 当前的是ImageA */
    /* 关闭当前所有使用中断,或者方便一点直接全部关闭 */
    DisableAllIRQ();

    /* 修改DataFlash,切换至ImageIAP */
    SwitchImageFlag(IMAGE_IAP_FLAG);

    /* 等待打印完成 ,复位*/
    mDelaymS(10);
    SYS_ResetExecute();

    break;
  }
  case CMD_IAP_INFO:
  {
    uint8_t send_buf[20];
    PRINT("IAP_INFO \r\n");

    /* IMAGE FLAG */
    send_buf[0] = IMAGE_B_FLAG;

    /* IMAGE_SIZE */
    send_buf[1] = (uint8_t)(IMAGE_SIZE & 0xff);
    send_buf[2] = (uint8_t)((IMAGE_SIZE >> 8) & 0xff);
    send_buf[3] = (uint8_t)((IMAGE_SIZE >> 16) & 0xff);
    send_buf[4] = (uint8_t)((IMAGE_SIZE >> 24) & 0xff);

    /* BLOCK SIZE */
    send_buf[5] = (uint8_t)(FLASH_BLOCK_SIZE & 0xff);
    send_buf[6] = (uint8_t)((FLASH_BLOCK_SIZE >> 8) & 0xff);

    send_buf[7] = CHIP_ID & 0xFF;
    send_buf[8] = (CHIP_ID << 8) & 0xFF;
    /* 有需要再增加 */

    /* 发送信息 */
    OTA_IAP_SendData(send_buf, 20);

    break;
  }

  default:
  {
    OTA_IAP_CMDErrDeal();
    break;
  }
  }
}

/*********************************************************************
 * @fn      OTA_IAPReadDataComplete
 *
 * @brief   OTA 数据读取完成处理
 *
 * @param   index   - OTA 通道序号
 *
 * @return  none
 */
void OTA_IAPReadDataComplete(unsigned char index)
{
  PRINT("OTA Send Comp \r\n");
}

/*********************************************************************
 * @fn      OTA_IAPWriteData
 *
 * @brief   OTA 通道数据接收完成处理
 *
 * @param   index   - OTA 通道序号
 * @param   p_data  - 写入的数据
 * @param   w_len   - 写入的长度
 *
 * @return  none
 */
void OTA_IAPWriteData(unsigned char index, unsigned char *p_data, unsigned char w_len)
{
  unsigned char rec_len;
  unsigned char *rec_data;

  rec_len = w_len;
  rec_data = p_data;
  tmos_memcpy((unsigned char *)&iap_rec_data, rec_data, rec_len);
  Rec_OTA_IAP_DataDeal();
}

4.修改Link.Ld文件

在这里插入图片描述

//原本
MEMORY
{
	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K
	RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
}
//修改后
MEMORY
{
	FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 216K
	RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
}

三、OTA升级

1.合并文件

将BackupUpgrade_IAP 产生的 hex 文件和BackupUpgrade_JumpIAP 产生的 hex 文件还有刚才自己编写的APP程序的hex 文件进行合并为bin文件。
软件可以去沁恒官网下载:沁恒微电子

在这里插入图片描述

2.下载bin文件

在这里插入图片描述

3.APP升级程序

3.1 OTA升级工具

具体可以在官网下载:沁恒微电子
在这里插入图片描述

3.2 搜索蓝牙,连接

在这里插入图片描述

3.3 选择升级的固件

就是刚才APP程序产生的HEX文件

在这里插入图片描述
在这里插入图片描述

3.4 升级过程

在这里插入图片描述

3.5 升级完成

在这里插入图片描述

3.6 升级结果

RGB灯珠由颜色切换,变成了固定一个颜色点亮。

在这里插入图片描述


四、总结

今天主要讲了CH582蓝牙OTA的操作,感兴趣的可以去试试。

感谢你的观看!

在这里插入图片描述

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

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

相关文章

Leetcode 59.螺旋矩阵Ⅱ

1.题目 2.思路 &#xff08;借用代码随想录的图&#xff09; 1.我们将转一圈看作一个循环&#xff08;1->2->3->4->5->6->7->8 这是一个循环&#xff09; 2.在这个循环里&#xff0c;我们要画四条边&#xff08;上右下左&#xff09; 填充上行从左到右 填…

数据分析-Pandas画分布密度图

数据分析-Pandas画分布密度图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&#xff…

前后端分离项目,如何解决跨域问题?

跨域问题是前后端分离项目中非常常见的一个问题&#xff0c;举例来说&#xff0c;编程猫学习网站的前端服务跑在 8080 端口下&#xff0c;后端服务跑在 9002 端口下&#xff0c;那么前端在请求后端接口的时候就会出现跨域问题。 403 Forbidden 是HTTP协议中的一个状态码&#x…

编曲学习:钢琴编写 人性化、逻辑预制 工程音频导出

第8课 钢琴编写 人性化、逻辑预制 工程音频导出小鹅通-专注内容付费的技术服务商https://app8epdhy0u9502.pc.xiaoe-tech.com/live_pc/l_65e30339e4b064a8cfe56001?course_id=course_2XLKtQnQx9GrQHac7OPmHD9tqbv 音乐创作中,有思路时可以不套学习到的公式,没有思路时可以套…

VMware下载与安装

准备一个Linux的系统&#xff0c;成本最低的方式就是在本地安装一台虚拟机&#xff0c;VMware是业界最好用的虚拟机软件之一 官网&#xff1a;https://www.vmware.com/ 下载页面&#xff1a;https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html …

前端学习之列表标签

目录 有序列表 结果 无序标签 结果 数据标签 结果 有序列表 &#xff08;注&#xff1a;注释是解释&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title> </…

MySQL数据库管理

本章内容&#xff1a; 掌握数据库的查看方法掌握MySQL 库和表的创建和删除方法掌握MySQL 增删改查常见操作掌握MySQL 用户权限 1.1使用MySQL数据库 在熟悉安装及访问MySQL 数据库以后&#xff0c;接下来将学习MySQL 数据库的基本操作&#xff0c; 这也是在服务器运维工作工不…

windows使用pyenv

1、前言 虽然anaconda比pyenv相比有更好的python安装体验&#xff0c;但是有一个比较严重的问题的就是&#xff0c;他的python版本跨度不够大&#xff0c;一些老一些的项目的python版本找不到&#xff0c;比如py12306要求的python版本是3.6&#xff0c;在anaconda却找不到这个版…

10.网络文件系统( NFS)使用

网络文件系统&#xff08; NFS&#xff09; 使用 NFS 优点&#xff1a; 开发过程中不受开发板空间的限制&#xff0c;直接使用网络文件就像使用本地文件一样&#xff1b;调试过程中避免一一将编译后的应用程序和库文件复制到开发板上。 在开发板中使用网络文件系统可以为开发和…

python 网络库集锦

目录 通用网络库 网络爬虫框架 1.功能齐全的爬虫 2.其他 HTML/XML解析器 1.通用 2.清理 文本处理 自然语言处理 浏览器自动化与仿真 多重处理 异步网络编程库 队列 云计算 网页内容提取 WebSocket DNS解析 计算机视觉 通用网络库 1.urllib -网络库(stdlib)。…

RabbitMQ - 03 - Work消息模型

目录 部署demo项目 什么是Work消息模型 实现Work消息模型 1.创建队列 2.生产者代码 3.消费者代码 4.配置yml 部署demo项目 通过消息队列demo项目进行练习 相关配置看此贴 http://t.csdnimg.cn/hPk2T 注意 生产者消费者的yml文件也要配置好 什么是Work消息模型 工作…

Python实现线性查找算法

Python实现线性查找算法 以下是使用 Python 实现线性查找算法的示例代码&#xff1a; def linear_search(arr, target):"""线性查找算法:param arr: 要搜索的数组:param target: 目标值:return: 如果找到目标值&#xff0c;返回其索引&#xff1b;否则返回 -1…

DJI RONIN 4D摄像机mov无法播放的修复方法

DJI大疆是无人机领域的一哥&#xff0c;最近几年大疆除了巩固无人机方面的技术实力还额外加强了其它领域产品的开发&#xff0c;而RONIN 4D的发布说明了大疆进军影视级的决心和实力。下边来看下DJI RONIN 4D生成的MOV文件无法播放的修复方法。 故障文件: 237.1G MOV文件 故障…

Java高频面试之并发篇

有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家 Java高频面试之总纲篇 Java高频面试之集合篇 Java高频面试之异常篇 Java高频面试之并发篇 Java高频面试之SSM篇 Java高频面试之Mysql篇 Java高频面试之Redis篇 Java高频面试之消息队列与分布式篇…

实时工业控制系统的创新整合:PLC4X与CnosDB的高效数据采集与存储

在当代工业自动化系统中&#xff0c;实时监测和数据分析变得至关重要。本文将介绍如何通过集成Apache PLC4X与CnosDB&#xff0c;实现对工业控制系统中的PLC设备进行高效数据采集和存储&#xff0c;为工程师们提供更强大的数据分析和监测工具。 PLC的定义 PLC是可编程逻辑控制…

【前端】vscode快捷键和实用Api整理

vscode的快捷键 创建a.html 生成模板 !回车 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" …

MySQl基础入门⑦

上一章知识内容 分析数据且区分数据类型 看下表分析数据的特征&#xff0c;根据其特征确定相应的数据类型。 分析以上表格特征&#xff0c;确定数据类型&#xff0c;并对数据进行分类。分析数据后按固定长度字符串、可变长度字符串、整数、固定精度小数和日期时间数据类型对数…

稀碎从零算法笔记Day14-LeetCode:同构字符串

题型&#xff1a;字符串、哈希表 链接&#xff1a;205. 同构字符串 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那…

【算法面试题】-04

执行时长 def min_execution_time(n, size, tasks):a 0ans sizei 0while i < size:tmp tasks[i]a tmpif a < n:a 0else:a - ni 1ans a // nif a % n ! 0:ans 1return ans# 读取输入 n int(input()) size int(input()) tasks list(map(int, input().split()))…

Unity使用Addressable热更新

先看热更新的gif: Addressable是Unity推出的打ab包方案。不需要手动写AB打包脚手架了&#xff0c;不需要关心依赖&#xff0c;这也简化了ab热更新的流程。Addressable打包需要先将资源放入group中&#xff0c;按group来打包&#xff0c;每个group对应一个ScriptableObject的配置…