STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56
STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56
1. STM32F407 BootLoader 中的 Flash 擦除功能详解
在嵌入式系统中,BootLoader 的设计是非常关键的部分,它负责引导主程序的启动、升级以及安全管理。而在 STM32F407 等 MCU 上实现 BootLoader,Flash 操作则是其中的核心功能之一。本文将重点介绍 STM32F407 上的 Flash 擦除功能,并给出实现过程和注意事项。
一、STM32F407 Flash 结构概述
STM32F407 芯片采用 Cortex-M4 内核,内置 Flash 存储用于程序和数据存储。STM32F407 的 Flash 大小为 512KB,分为若干个扇区(Sector),每个扇区的大小并不相同:
- 扇区 0 到 3:16KB 每扇区
- 扇区 4:64KB
- 扇区 5 到 11:128KB 每扇区
这种不同大小的扇区设计适合不同的应用需求,比如小容量扇区用于存储配置数据,大容量扇区用于存储固件。
二、Flash 擦除的原理
Flash 擦除是将 Flash 中的某个扇区的数据重置为 0xFF
。由于 Flash 的物理特性,写操作只能将位设置为 0,而擦除操作将位恢复为 1。因此,在更新 Flash 数据时,通常需要先擦除再写入。
在 STM32F407 中,Flash 擦除只能以扇区为单位进行,这意味着无法擦除扇区中的部分数据。每次擦除扇区时,整个扇区的数据都会被清空。
三、Flash 擦除的操作流程
在 STM32F407 的 BootLoader 中实现 Flash 擦除,一般需要遵循以下步骤:
- 解锁 Flash 控制寄存器:在进行任何 Flash 操作之前,需要解锁 Flash 的写保护功能。
- 等待 Flash 空闲:检查 Flash 状态寄存器,确保没有其他操作正在进行。
- 启动扇区擦除:设置目标扇区并启动擦除命令。
- 等待擦除完成:监控状态寄存器中的
BSY
位,等待擦除操作完成。 - 锁定 Flash 控制寄存器:完成操作后,将 Flash 锁定,以防止误操作。
四、Flash 擦除实现代码
以下是 STM32F407 上实现 Flash 擦除的代码示例。本文假设使用了 STM32 标准外设库,便于调用硬件寄存器。
#include "stm32f4xx.h"/*** @brief 擦除 Flash 指定扇区* @param sector 要擦除的扇区编号* @retval 0 表示成功,-1 表示失败*/
int Flash_EraseSector(uint8_t sector)
{// 解锁 Flash 控制寄存器FLASH_Unlock();// 等待 Flash 空闲while (FLASH_GetStatus() == FLASH_BUSY);// 擦除操作FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);FLASH_Status status = FLASH_EraseSector(sector, VoltageRange_3);// 等待擦除完成if (status != FLASH_COMPLETE){FLASH_Lock();return -1; // 擦除失败}// 锁定 Flash 控制寄存器FLASH_Lock();return 0; // 擦除成功
}
五、代码详解
FLASH_Unlock()
:此函数用于解锁 Flash 控制寄存器,以允许擦除和写入操作。FLASH_GetStatus()
:用于检查 Flash 是否处于忙碌状态。擦除操作会占用一定的时间,必须等待 Flash 空闲后才能继续。FLASH_ClearFlag()
:清除可能出现的错误标志位,确保擦除操作不会受到之前错误的影响。FLASH_EraseSector()
:这是 STM32 库中的擦除函数,输入参数为扇区编号和电压范围(STM32F4 系列通常使用VoltageRange_3
)。FLASH_Lock()
:操作完成后锁定 Flash 控制寄存器,以防止意外操作。
六、Flash 擦除的注意事项
- 擦除单位:在 STM32F407 中,Flash 擦除只能以扇区为单位进行,无法进行字节或页的擦除。因此,设计 BootLoader 时要合理分配数据的存储位置,避免不必要的擦除操作。
- 电压范围:擦除 Flash 时,电源电压必须在一定范围内,低电压下可能导致擦除失败甚至损坏 Flash。STM32F407 提供了多种电压模式,通常选择
VoltageRange_3
即可。 - 错误处理:Flash 擦除失败可能会出现各种错误,例如写保护、编程错误等。建议在擦除操作前后清除并检查错误标志。
- 数据备份:在擦除前备份必要的数据。在 BootLoader 中进行固件更新时,最好将旧固件暂时保存至其他存储介质中,以应对擦除或写入失败的情况。
- 上电时间:Flash 擦除时间较长,尤其是 128KB 扇区,因此在设计 BootLoader 时需要考虑电源的持续供电能力,避免中途掉电。
2. BootLoader Flash划分
由下图可以看出本BootLoader的Flash的划分,其中划给BootLoader为32KB,从0x0800 0000-0x0800 7FFF,占用Sector 0-1两个段。App应用程序占用0x0800 8000-0x080F FFFF,总共10个Sector。
下面是BootLoader的跳转过程,关于具体的跳转过程,可以参考上一篇文章: STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55
3. 0x56命令介绍–Flash擦除
在本篇文章,我们的主要是介绍0x56的命令,这个命令主要是在BootLoader中擦除指定Flash Sector的命令。
通过上位机发送8 Byte的数据,其中第1 Byte为整个数据的长度,第2Byte为指令码,第3 Byte为要擦除Flash的起始Sector序号,第4 Byte是擦除Sector的长度,第5-8 Byte为前6个Byte的CRC校验值。上位机通过串口UART发送给下位机,下位机回复地址是否擦除成功的标志。
下面是发送命令过程中上位机与BootLoader之间的交互。
4. Flash擦除命令程序设计
下面我们来进行程序设计,下面是读取上位机指令,并解析指令的过程,通过switch case判断执行哪种命令。目前通过上位机执行BL_FLASH_ERASE指令,然后执行bootloader_handle_flash_erase_cmd(bl_rx_buffer)函数。
下面是代码的详细注释和分析:
bootloader_uart_read_data
函数
该函数用于从主机接收命令数据并调用相应的处理函数来执行命令。它是 BootLoader 的核心输入接口,通过 UART 接收主机的命令,并根据命令类型分发到对应的处理函数。
void bootloader_uart_read_data(void)
{uint8_t rcv_len = 0;// 打印调试信息,提示接收到命令printmsg_Host("BL_DEBUG_MSG: Receive CMD\n\r");// 无限循环,持续接收命令并处理while (1){// 将 LED2 熄灭,表示进入命令接收状态HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);// 清空接收缓冲区,防止残留数据干扰memset(bl_rx_buffer, 0, 200);// 读取命令包的第一个字节,即命令的长度字段HAL_UART_Receive(C_UART, bl_rx_buffer, 1, HAL_MAX_DELAY);// 获取命令包长度,长度字节位于第一个字节rcv_len = bl_rx_buffer[0];// 根据命令包长度读取剩余的字节HAL_UART_Receive(C_UART, &bl_rx_buffer[1], rcv_len, HAL_MAX_DELAY);// 根据命令码执行相应的命令处理函数switch (bl_rx_buffer[1]){case BL_GET_VER:// 处理获取版本命令bootloader_handle_getver_cmd(bl_rx_buffer);break;case BL_GET_HELP:// 处理获取帮助命令bootloader_handle_gethelp_cmd(bl_rx_buffer);break;case BL_GET_CID:// 处理获取芯片 ID 命令bootloader_handle_getcid_cmd(bl_rx_buffer);break;case BL_GET_RDP_STATUS:// 处理获取读保护状态命令bootloader_handle_getrdp_cmd(bl_rx_buffer);break;case BL_GO_TO_ADDR:// 处理跳转到地址命令bootloader_handle_go_cmd(bl_rx_buffer);break;case BL_FLASH_ERASE:// 处理 Flash 擦除命令bootloader_handle_flash_erase_cmd(bl_rx_buffer);break;default:// 未知命令,打印调试信息printmsg("BL_DEBUG_MSG: Invalid command code received from host \n");break;}}
}
- 初始化和循环:函数会无限循环等待主机发送的命令并进行解析。
- 数据接收:首先读取一个字节的长度字段,然后根据这个长度读取完整的命令数据。
- 命令解析:通过
switch
语句解析命令码,并调用相应的处理函数。 - 错误处理:如果命令码不在预期范围内,打印调试信息。
bootloader_handle_flash_erase_cmd
函数
该函数处理 BL_FLASH_ERASE
命令,通过接收到的参数执行 Flash 擦除操作。
void bootloader_handle_flash_erase_cmd(uint8_t *pBuffer)
{uint8_t erase_status = 0x00;printmsg("BL_DEBUG_MSG: bootloader_handle_flash_erase_cmd\n");// 获取命令包的总长度uint32_t command_packet_len = bl_rx_buffer[0] + 1;// 从命令包中提取主机发送的 CRC32 值uint32_t host_crc = *((uint32_t *)(bl_rx_buffer + command_packet_len - 4));// 验证接收到的数据包的 CRC 校验if (!bootloader_verify_crc(&bl_rx_buffer[0], command_packet_len - 4, host_crc)){// 如果校验成功printmsg("BL_DEBUG_MSG: checksum success !!\n");// 发送 ACK 响应,确认命令长度为 1 字节bootloader_send_ack(pBuffer[0], 1);// 打印擦除信息:初始扇区和扇区数量printmsg("BL_DEBUG_MSG: initial_sector: %d, no_of_sectors: %d\n", pBuffer[2], pBuffer[3]);// 熄灭 LED 表示擦除操作开始HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);// 调用 `execute_flash_erase` 执行擦除操作,传入初始扇区和扇区数量erase_status = execute_flash_erase(pBuffer[2], pBuffer[3]);// 重新点亮 LED 表示擦除操作完成HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);// 打印擦除操作的状态printmsg("BL_DEBUG_MSG: flash erase status: %#x\n", erase_status);// 发送擦除状态回主机bootloader_uart_write_data(&erase_status, 1);}else{// 如果 CRC 校验失败,发送 NACK 响应printmsg("BL_DEBUG_MSG: checksum fail !!\n");bootloader_send_nack();}
}
- 参数解析和 CRC 校验:从命令包中提取 CRC 值并进行校验,确保数据完整性。
- 发送 ACK 或 NACK:根据 CRC 校验结果发送确认或拒绝响应。
- 擦除操作:如果校验通过,提取擦除操作的初始扇区和扇区数量,然后调用
execute_flash_erase
函数执行擦除操作。 - 状态回传:擦除完成后,将操作状态通过 UART 返回给主机。
execute_flash_erase
函数
execute_flash_erase
函数负责执行 Flash 的具体擦除操作,支持部分扇区擦除和全片擦除。
uint8_t execute_flash_erase(uint8_t sector_number, uint8_t number_of_sector)
{// STM32F407 芯片共有 12 个扇区(0 到 11)// 如果 sector_number = 0xff,表示全片擦除FLASH_EraseInitTypeDef flashErase_handle;uint32_t sectorError;HAL_StatusTypeDef status;// 如果指定的扇区数量超过有效范围,返回无效扇区if (number_of_sector > 11)return INVALID_SECTOR;// 检查扇区号是否有效if ((sector_number == 0xFF) || (sector_number <= 11)){// 判断是否全片擦除if (sector_number == (uint8_t)0xFF){flashErase_handle.TypeErase = FLASH_TYPEERASE_MASSERASE;}else{// 部分扇区擦除// 计算剩余可擦除的扇区数uint8_t remaining_sector = 12 - sector_number;if (number_of_sector > remaining_sector){number_of_sector = remaining_sector;}flashErase_handle.TypeErase = FLASH_TYPEERASE_SECTORS;flashErase_handle.Sector = sector_number;flashErase_handle.NbSectors = number_of_sector;}// 设置擦除银行(STM32F4 系列有两个 Flash Bank,选取 Bank 1)flashErase_handle.Banks = FLASH_BANK_1;// 解锁 Flash 寄存器,允许擦除操作HAL_FLASH_Unlock();// 设置电压范围,适应 STM32F4 的工作电压范围flashErase_handle.VoltageRange = FLASH_VOLTAGE_RANGE_3;// 执行擦除操作,传入擦除配置和错误记录变量status = (uint8_t)HAL_FLASHEx_Erase(&flashErase_handle, §orError);// 锁定 Flash 控制寄存器,防止误操作HAL_FLASH_Lock();return status; // 返回擦除操作的状态}return INVALID_SECTOR; // 如果无效扇区,返回无效状态
}
- 全片和部分擦除:函数支持全片擦除(
sector_number == 0xFF
)和从指定扇区开始的部分擦除。 - 擦除范围检查:确保扇区数量不超过剩余的可擦除范围。
- 解锁和锁定:在擦除操作前解锁 Flash 控制寄存器,擦除完成后重新锁定。
- 电压范围设置:设置为
FLASH_VOLTAGE_RANGE_3
,适配 STM32F4 的工作电压。 - 状态返回:返回擦除状态,以便调用方确认擦除是否成功。
总结
bootloader_uart_read_data
:从 UART 接收命令,解析命令码并调用对应的命令处理函数。bootloader_handle_flash_erase_cmd
:处理 Flash 擦除命令,进行 CRC 校验
5. 实战演练
下面是上位机的命令菜单,通过在终端调用Python脚本,然后在终端输入下位机连接的串口号,即可进入命令界面,目前可支持如下命令:
下面我们执行命令–7,执行BL_FLASH_ERASE,即为在BootLoader中执行Flash擦除命令0x56,第一步输入要开始擦除的Sector起始段,第二步输入擦除Sector的长度,最后BootLoader向上位机返回是否擦除成功的Status。
下面我们来看Flash擦除命令是否擦除成功,用STM32CubeProgrammer工具,用ST-LINK连接Debug口,上一步中我们擦除的Sector 3的Flash,这里面我们输入Sector 2的起始地址0x0800 C000,可以看出全部擦除为FF。至此Flash擦除命令已经讲完。
6. 结论
Flash 擦除是 STM32F407 BootLoader 设计中的重要环节,它为固件更新和数据存储提供了基础。本文详细介绍了 Flash 擦除的原理和实现方法,并提供了相关代码和注意事项。在实际应用中,设计人员可以根据需要将擦除功能整合至 BootLoader 的主流程中,确保系统的可靠性和可升级性。
希望本篇文章能为读者在 STM32F407 的 BootLoader 开发中提供一些帮助,若有疑问,欢迎交流讨论。
7. 系列文章
STM32 BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建
STM32 BootLoader 刷新项目 (二) 方案介绍
STM32 BootLoader 刷新项目 (三) 程序框架搭建及刷新演示
STM32 BootLoader 刷新项目 (四) 通信协议
STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51
STM32 BootLoader 刷新项目 (六) 获取帮助-命令0x52
STM32 BootLoader 刷新项目 (七) 获取芯片ID-0x53
STM32 BootLoader 刷新项目 (八) 读取Flash保护ROP-0x54
STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55
相关文章:

STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56
STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56 1. STM32F407 BootLoader 中的 Flash 擦除功能详解 在嵌入式系统中,BootLoader 的设计是非常关键的部分,它负责引导主程序的启动、升级以及安全管理。而在 STM32F407 等 MCU 上实现 BootLoader&…...

POI word转pdf乱码问题处理
1.使用poi 转换word文档成pdf 导入依赖 <dependency><groupId>com.aspose</groupId><artifactId>words</artifactId><version>16.8.0</version></dependency>2.代码实现: SneakyThrowspublic void wordToPdf(String docPath,…...

【GeekBand】C++设计模式笔记11_Builder_构建器
1. “对象创建” 模式 通过 “对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。典型模式 Factory MethodAbstract …...

面试经典 150 题:20、2、228、122
20. 有效的括号 参考代码 #include <stack>class Solution { public:bool isValid(string s) {if(s.size() < 2){ //特判:空字符串和一个字符的情况return false;}bool flag true;stack<char> st; //栈for(int i0; i<s.size(); i){if(s[i] ( |…...

SQL面试题——持续增长问题
持续增长我们也可以称之为连续增长,本质上还是连续类的问题,前面我们已经介绍过 SQL面试题——最大连续登陆问题 SQL面试题——球员连续四次得分 SQL面试题——间隔连续问题 SQL面试题——蚂蚁SQL面试题 连续3天减少碳排放量不低于100的用户 你可以看看之前的文章,了解…...

nginx源码安装配置ssl域名
nginx源码安装 下载 wget http://nginx.org/download/nginx-1.24.0.tar.gz 解压 tar -zxvf nginx-1.24.0.tar.gz 下载openssl apt install openssl 安装nginx cd nginx-1.24.0 sudo apt-get install libpcre3 libpcre3-dev ./configure --prefix=/home/nginx24 --with-http_ss…...

每日一博 - Java的Shallow Copy和Deep Copy
文章目录 概述创建对象的5种方式1. 通过new关键字2. 通过Class类的newInstance()方法3. 通过Constructor类的newInstance方法4. 利用Clone方法5. 反序列化 Clone方法基本类型和引用类型浅拷贝深拷贝如何实现深拷贝1. 让每个引用类型属性内部都重写clone()方法2. 利用序列化 概述…...

.netcore + postgis 保存地图围栏数据
一、数据库字段 字段类型选择(Type) 设置对象类型为:geometry 二、前端传递的Json格式转换 前端传递围栏的各个坐标点数据如下: {"AreaRange": [{"lat": 30.123456,"lng": 120.123456},{"lat": 30.123456…...

【AI图像生成网站Golang】项目介绍
AI图像生成网站 目录 一、项目介绍 二、雪花算法 三、JWT认证与令牌桶算法 四、项目架构 五、图床上传与图像生成API搭建 六、项目测试与调试(等待更新) 简介 本教程将手把手教你如何从零开始构建一个简单的AI图像生成网站。网站主要包含用户注册、图像生成、分类管理等…...

对称加密算法DES的实现
一、实验目的 1、了解对称密码体制基本原理 2、掌握编程语言实现对称加密、解密 二、实验原理 DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密…...

Spring Boot 启动时修改上下文
Spring Boot 启动时修改上下文 为了让项目在启东时,加载到封装的JAR中的国际化文件在封装JAR是增加以下配置类可用于更改启动上下文中的信息依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoco…...

传奇996_19——常用函数
打印 打印到公告 lua版 sendmsg(*actor*, ConstCfg.notice.own, {"Msg":"<font color\#ff0000\>即将更新属性2222!!!</font>","Type":9}) sendmsg(*actor*, 1, {"Msg":"<fon…...

计算机毕业设计Python+Neo4j知识图谱医疗问答系统 大模型 机器学习 深度学习 人工智能 大数据毕业设计 Python爬虫 Python毕业设计
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

【Python】如何设置VSCode中的Pylint,消除各种没有必要的警告
前言 最近打开VSCode,编辑之前创建的Python项目,突然发现多了一堆报错和警告,如下图所示。 就非常吓人,因为之前这个项目是没有任何报错的,我赶紧试着运行了一下,还好,可以正常运行,…...

游戏引擎学习第14天
视频参考:https://www.bilibili.com/video/BV1iNUeYEEj4/ 1. 为什么关注内存管理? 内存分配是潜在的失败点: 每次进行内存分配(malloc、new等)时,都可能失败(例如内存不足)。这种失败会引入不稳…...

关于mysql中的锁
mysql中包含的锁分为: 一、全局锁 二、表锁 三、行锁 一、全局锁 全局锁的力度是最大的,全局锁对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句…...

机器学习-4:机器学习的建模流程
机器学习的建模流程 流程为: 原始数据 --> 数据预处理 --> 特征工程 --> 建模 --> 验证。 原始数据收集 所有AI或机器学习的基础就是数据,没有数据就什么都做不了,在搭建一个系统之前首要考虑的就是有没有足够多的数据可以支撑这…...

Android 6年经验面试总结 2024.11.15
背景:深圳 面过12家中大厂、4家中小厂,通过4家中大厂,2家offer。 针对六年的求职面试总结:项目经验70%30%基础(基础应该必会) 对于上来就问八股文的公司,对于已经工作了5年以上的开发来说&…...

R语言数据分析可视化——summarytools包的使用
R语言中的summarytools包通过提供能够用最少的代码生成数据全面摘要的功能,使数据分析更加简单。summarytools包提供了一种简单的方法来生成数据集的摘要统计信息,包括描述性统计、频率表、交叉表、缺失值、异常值、相关性、线性回归、ANOVA、卡方检验等。本文将介绍如何使用…...

转型一年半,虎牙直播的第二增长曲线喜忧参半
文:互联网江湖 作者:刘致呈 最近,虎牙公司(NYSE:HUYA)公布了2024年第三季度财报。 表现怎么样呢?从财务数据上看,这份成绩单有点不尽人意。 报告期内,虎牙实现营收15.38亿元&…...

makefile笔记
makefile 在 Makefile 中,预定义的变量(也称为内置变量)提供了对构建过程中的默认值和特殊值的访问。这些变量通常由 Make 自动设置,并且可以覆盖它们以改变 Make 的行为。下面是 Make 环境中常见的几个内置变量及其用途…...

Rewar Model的输出(不包含训练)
这里写自定义目录标题 介绍模型推理的输出过程方案原始Token输出RM输出(回归任务) 介绍 奖励函数模型 (Reward Model) 是人工智能 (AI) 中的一种方法,模型因其对给定提示的响应而获得奖励或分数。现在的文章清一色的讲解RM的训练,…...
Python调用API翻译Excel中的英语句子并回填数据
一、问题描述 最近遇到一个把Excel表中两列单元格中的文本读取,然后翻译,再重新回填到单元格中的案例。大约有700多行,1400多个句子,一个个手动复制粘贴要花费不少时间,而且极易出错。这时,我们就可以请出…...

SQL面试题——抖音SQL面试题 最大在线用户数
最大在线用户数 下面的数据记录了一个直播平台上用户进入平台和离开平台的情况 +---+-------------------+-----+ | id| etime| type| +---+-------------------+-----+ | 1|2021-06-10 10:00:00|enter| | 1|2021-06-10 19:00:00|leave| | 2|2021-06-10 11:0…...

前端知识点---Window对象(javascript)了解
Window对象 在JavaScript中,当你在非严格模式下的全局作用域中使用this时,它会引用全局对象。在浏览器环境中,这个全局对象就是Window。 01什么是 Window 对象? Window 是浏览器提供的一个全局对象,它代表了浏览器的…...

llama factory lora 微调 qwen2.5 7B Instruct模型
项目背景 甲方提供一台三卡4080显卡 需要进行qwen2.5 7b Instruct模型进行微调。以下为整体设计。 要使用 LLaMA-Factory 对 Qwen2.5 7B Instruct模型 进行 LoRA(Low-Rank Adapters)微调,流程与之前提到的 Qwen2 7B Instruct 模型类似。LoRA …...

类和对象——拷贝构造函数,赋值运算符重载(C++)
1.拷⻉构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。 // 拷贝构造函数//d2(d1) Date(const Date& d) {_year d._yea…...

Android 关于使用videocompressor库压缩没有声音和异常的问题
原库地址 https://gitcode.com/gh_mirrors/vi/VideoCompressor/overview 这个库用起来比较方便,使用Android原生的MediaCodecmp4parser的方式进行压缩,不用接入so库也不用适配cpu 问题 接口库后你会发现过时了,所以你一阵捣鼓后你发现压缩…...

LeetCode-215.数组中的第K个最大元素
. - 力扣(LeetCode)给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问…...

『OpenCV-Python』视频的读取和保存
点赞 + 关注 + 收藏 = 学会了 推荐关注 《OpenCV-Python专栏》 上一讲介绍了 OpenCV 的读取图片的方法,这一讲简单聊聊 OpenCV 读取和保存视频。 视频的来源主要有2种,一种是本地视频文件,另一种是实时视频流,比如手机和电脑的摄像头。 要读取这两种视频的方法都是一样的…...