STM32内置Flash
一、原理

利用flash存储用户数据需要注意查看,用户数据是否会覆盖芯片运行程序。
IAP(在程序中编程)利用程序修改程序本身,和OTA是一个原理。IAP在程序中编程支持任意一种通信下载。
ICP(在电路中编程,通过外接引脚下载),每次下载会将程序完全更新。JTAG和SWD为仿真器下载程序,例如JLINK-JTAG和STLNIK-SWD。串口下载(手册中系统Bootload硬件有说明)使用的时系统存储器的Bootload。
1、STM32的存储器映像

2、个人理解的Bootload原理

3、STM32中容量产品闪存容量分布情况

可以看到的主存储器和信息块属于flash,闪存存储器不属于flash,相当于单独的外设。闪存存储器相当于上面信息块和主存储器的管理员,用于管理擦除和编程(读取flash直接进行指针读取即可,无需借用存储器)。
- STM32内部的flash只分成了页,起始地址为0x0800 0000,直到0x0801 FFFF,一共128K,每K一页,可以看到,以000、400、800、C00结尾的都是页起始地址。
- 系统存储区容量为2K,起始地址,存储系统Bootload内容。容量2K。
- 用户选项字节为16Byte,起始地址为0x1FFF F800 ,容量16Byte
- 用户选项字节和启动程序代码(系统存储区)也是flash,但是不计算在闪存容量中,闪存容量一般指程序存储区flash。
- 闪存存储器接口寄存器起始地址为0x4002 2000,属于外设,在外设存储区内


4、flash基本结构

5、flash擦除和编程详细内容
5.1、flash解锁

通过键寄存器写入指定的键值实现。RDPRT是解除读保护的密钥。将flash_cr重新置lock=1可锁住flash_cr。
5.2、使用指针访问存储器
volatile:
- 在c语言中表示易变的数据,防止编译器优化(编译器自动去除无用的繁杂代码)。
- STM32中有工作组寄存器,类似cache,工作速度快,但是使用的变量可能是程序中变量的映射。若程序为多线程或者中断,改变了此变量,会造成工作组寄存器和源变量值不一致,导致程序异常。通过volatile可以高速编译器变量需要实时注意是否更改。
通过*((__IO uint16_t *)(0x0800 0000))通过程序取出地址0x0800 0000地址的寄存器的数据,并通过uint16_t类型(可根据具体需求)返还。也可通过此形式直接指定指定地址的值。flash安全程度较高,需要提前解锁。若写入SRAM则不需要。
5.3、程序存储区编程

写入只能通过地址防止,进行半字的写入,一次2byte。写入32bit需要分两次,写入8bit可以先进行读出在使用读改写的方式写入。
5.4、程序存储区页擦除,flash写入前必须全擦除为1,只能写入0,不能写入1,根据手册的擦除最小单位进行。

5.5、全擦除

6、选项字节的组织和用途
可以看到表中,有数据和n数据,表示写入正常数据时要在对应的n位写入反码,这样才能够正常有效写入。 写入反码的过程,硬件会自动计算并写入。
WRP:配置flash程序存储区写保护,每位对应保护4个存储页,4*8*4 = 128页,刚好对应中容量的最大字节128K。



6.1 、选项字节编程

解锁flash锁后还需要解锁选项字节的锁(也需要先写入Key1,再写入Key2),之后才能操作选项字节。
6.2、选项字节擦除

7、期间的电子签名

可以通过指定签名运行指定程序来限制程序被盗。
8、手册

在SystemInit已经打开了HSI。



因为flash的原因,默认写保护使能为0,不使能写保护为1。


9、编程
在库文件.c的头部有说明,库中后期增加了XL加大容量的芯片,部分函数只能使用于加大容量的芯片。
9.1存储格式
- Intel存储格式为小端存储:即低字节在低地址,高字节在高地址。即L_Byte(低地址) +H_Byte(高地址) ,例如0x482A = 18594。小端存储为 0x48 0x2A。
| intel格式(小端存储) | bit | ||||||||
| Byte | 1 | 0x2A(L_Byte) | |||||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
| 2 | 0x48(H_Byte) | ||||||||
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | ||
- Motorola存储格式为大端存储:即高字节在低地址,低字节在高地址。即H_Byte(低地址) +L_Byte(高地址) ,例如0x482A = 18594。大端存储为 0x2A 0x48。
| Motorola格式(大端存储) | bit | ||||||||
| Byte | 1 | 0x48(H_Byte) | |||||||
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | ||
| 2 | 0x2A(L_Byte) | ||||||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
- 字节内部相同高bit在左,低bit在右。例如大端存储和小端字节内存储格式相同。
| 大端小端相同(bit位相同) | |||||||||||||||
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0x48(H_Byte) | 0x2A(L_Byte) | ||||||||||||||
9.2为了区分程序存储空间和使用flash存储用户数据的空间大小,限制程序flash。

如果想写一个自定义的Bootload程序在尾部,也可以通过此处更改烧写程序的起始位置,但是要计算Bootload程序的大小、正式程序的大小,防止空间不足 。
右边是片上ram的内存。
9.3 下载配置,根据需要配置下载时如何擦除程序。使用IAP选择页擦除(STM32为扇区擦除选项)


前三个数为程序占用闪存的大小,后面两个相加为占用SRAM的大小。

目前可以看到我这个程序占用flash的大小为5.03KB=5150.72B,flash地址为0x0800 0000,偏移5148B,实际存储空间到0x0800 141C左右。

使用软件核实程序的确存储到了0x0800141C左右。
二、程序实例,STM32 ST-LINK Utility使用见主页文章
1、测试程序中的Flash基本操作单元的功能,flash页写入、flash读取、页擦除、全部擦除。(按键1用PB11、按键2用PB10)

1.1 测试1 (程序中#if (1))
1、程序烧写完成上电,查看OLED显示内容,是否为flash首地址的32bit、16bit、8bit(小端存储,低字节在低地址)




2、按下按键PB11可以看到和程序中相同flash程序存储区全部擦除,注意再次烧写程序时需要关闭STM32 ST-LINK Utility,防止冲突

3、重新烧写程序,按下按键PB12可以看到程序存储区中flash中0x0800 0400的页被删除,直到0x0800 0800(每个扇区1K),擦除后为FF

1.2、测试2 (程序中改#if (0))
可以看到0x0800FC00页地址原来的数据。

按下按键PB11或PB10,0x0800FC00和0x0800FC10所在地址的数据写为0x00000000(32bit)和0x0F0F(16bit)。

程序
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Button.h"
#include "MyFlash.h"
int main(void){OLED_Init();ButtonPB11PB10_Init();//测试程序存储区flash数据读取OLED_ShowString(1,1,"Flash:");//OLED_ShowHexNum(2,1,MyFlash_ReadWord(0x08000000),8);OLED_ShowHexNum(3,1,MyFlash_ReadHalfWord(0x08000000),4);OLED_ShowHexNum(4,1,MyFlash_ReadByte(0x08000000),2);while(1){#if (0) //为真运行//测试程序存储区flash全部擦除和页擦除if(GetButtonPB11PB10() == 1){MyFlash_EraseAll();}else if(GetButtonPB11PB10() == 2){MyFlash_ErasePage(0x08000400);//擦除第二页数据}#else //测试程序存储区flash写入字和半字if((GetButtonPB11PB10() == 1) || (GetButtonPB11PB10() == 2)){MyFlash_ErasePage(0x0800FC00);MyFlash_ProgramWord(0x0800FC00,0x00000000);//地址0x0800FC00写入半字0x00000000MyFlash_ProgramHalfWord(0x0800FC10,0x0F0F);//地址0x0800FC00写入半字0x0F0FOLED_ShowString(4,4,"W");Delay_ms(500);OLED_ShowString(4,4," ");}#endif}return 0;
}
MyFlash.c
#include "stm32f10x.h" // Device header
/*选项字节配置可通过外部的STM32 ST-Link Utility,用户自行使用选项字节容易出现问题*//*** @brief 读取Flash指定地址的字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint32_t MyFlash_ReadWord(uint32_t Address){//对于Address,通过(__IO uint32_t *)变为uint32_t类型数据的指针,通过*取该地址的值return *((__IO uint32_t *)(Address)) ;
}/*** @brief 读取Flash指定地址的半字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint16_t MyFlash_ReadHalfWord(uint32_t Address){//对于Address,通过(__IO uint16_t *)变为uint16_t类型数据的指针,通过*取该地址的值返回return *((__IO uint16_t *)(Address)) ;//uint16_t *表示指向uint16_t数据的指针
}/*** @brief 读取Flash指定地址的字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint8_t MyFlash_ReadByte(uint32_t Address){//对于Address,通过(__IO uint32_t *)变为uint32_t的指针,通过*取该地址的值return *((__IO uint8_t *)(Address)) ;
}/*** @brief 程序存储区flash全部擦除* @param * @arg * @param * @arg * @retval None*/
void MyFlash_EraseAll(void){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_EraseAllPages();//全部擦除FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash页擦除* @param Address:需要擦除的页地址* @arg * @param * @arg * @retval None*/
void MyFlash_ErasePage(uint32_t PageAddress){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ErasePage(PageAddress);//全部擦除FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash指定地址写字* @param * @arg * @param * @arg * @retval None*/
void MyFlash_ProgramWord(uint32_t Address,uint32_t WriteWord){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ProgramWord(Address,WriteWord);//指定地址写字FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash指定地址写半字* @param * @arg * @param * @arg * @retval None*/
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t WriteWord){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ProgramHalfWord(Address,WriteWord);//指定地址写半字,写入32bit地址的低地址,STM32flash小端存储,低字节在低位FLASH_Lock();//程序存储区flash页编程关锁
}
MyFlash.h
#ifndef __MYFLASH_H
#define __MYFLASH_H
#include "stm32f10x.h" // Device header
uint32_t MyFlash_ReadWord(uint32_t Address);
uint16_t MyFlash_ReadHalfWord(uint32_t Address);
uint8_t MyFlash_ReadByte(uint32_t Address);
void MyFlash_EraseAll(void);
void MyFlash_ErasePage(uint32_t PageAddress);
void MyFlash_ProgramWord(uint32_t Address,uint32_t WriteWord);
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t WriteWord);#endif
Button.c
/*** @brief 初始化引脚PB11地开信号接收Button,用于辅助测试看门狗* @param * @arg * @param * @arg * @retval None*/
void ButtonPB11PB10_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);
}/*** @brief 获取PB11的Button是否按下* @param * @arg * @param * @arg * @retval None*/
uint8_t GetButtonPB11PB10(void){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET){Delay_ms(10);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET);Delay_ms(10);return 1;}if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == RESET){Delay_ms(10);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == RESET);Delay_ms(10);return 2;}return 0;
}
Button.h
#ifndef __BUTTON_H
#define __BUTTON_H
#include "stm32f10x.h" // Device headeruint8_t GetButtonPB11PB10(void);
void ButtonPB11PB10_Init(void);
#endif
2、 读写内部Flash,通过Flash最后一页内存进行数据存储(上电读取内容、按键更新并写入内容)(按键1用PB11、按键2用PB10)
测试方法:
1、烧写程序后可以看到程序持续运行,此时、OLED显示程序存储区flash的最后一页存储的8Byte数据,第一次使用为0x00000000,可以看到指定的flash第一次读写标志位0xA5A5和0x00等数据已写入。

2、按键PB10可以看到程序存储区flash最后一页存储的前5字节当前数据变为0x1111;0x2222;0x3333;0x4444;按键PB11可以看到数据会自增。

程序
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Button.h"
#include "Store.h"
int main(void){OLED_Init();ButtonPB11PB10_Init();Store_Init();//显示当前Flash最后一页的数据OLED_ShowString(1,1,"Flash:");//while(1){uint8_t Key_Num = GetButtonPB11PB10();//测试按键控制Flash数据更新if(Key_Num == 1){//PB11for(uint8_t i=1;i<5;i++){Store_Data[i]++;}Store_Write();Store_Read();OLED_ShowString(4,1,"PB11");}else if(Key_Num == 2){//PB10Store_Data[1] = 0x1111;Store_Data[2] = 0x2222;Store_Data[3] = 0x3333;Store_Data[4] = 0x4444;Store_Write();Store_Read();OLED_ShowString(4,1,"PB10");}//显示Flash数据OLED_ShowHexNum(2,1,Store_Data[1],4);OLED_ShowHexNum(2,6,Store_Data[2],4);OLED_ShowHexNum(3,1,Store_Data[3],4);OLED_ShowHexNum(3,6,Store_Data[4],4);}return 0;
}
Store.c
#include "stm32f10x.h" // Device header
#include "MyFlash.h"#define STORE_START_ADDRESS 0x0800FC00 //写入页的起始地址
#define STORE_COUNT 512
#define STORE_FLAG 0xA5A5
uint16_t Store_Data[STORE_COUNT] = {STORE_FLAG,0};//用RAM对应存储Flash最后一页的数据/*** @brief flash最后一页数据初始化,第一次使用的话默认为0x00* @param * @arg * @param * @arg * @retval None*/
void Store_Init(void){//对于存储过数据的Flash我们默认使用最后一页的起始地址16bit作为标志if(MyFlash_ReadHalfWord(STORE_START_ADDRESS) == STORE_FLAG){//若不是第一次使用,读出数据for(uint16_t i=0;i<STORE_COUNT;i++){Store_Data[i] = MyFlash_ReadHalfWord(STORE_START_ADDRESS+i*2);}}else{//若是第一次使用,写入0xA5A5标志位和剩余的0x00MyFlash_ErasePage(STORE_START_ADDRESS);//擦除最后一页for(uint16_t i=0;i<STORE_COUNT;i++){MyFlash_ProgramHalfWord(STORE_START_ADDRESS+i*2,Store_Data[i]);//写入}}
}/*** @brief 将SAM的数据(Store_Data)写入flash最后一页* @param * @arg * @param * @arg * @retval None*/
void Store_Write(void){MyFlash_ErasePage(STORE_START_ADDRESS);//擦除最后一页for(uint16_t i=0;i<STORE_COUNT;i++){MyFlash_ProgramHalfWord(STORE_START_ADDRESS+i*2,Store_Data[i]);//写入}
}/*** @brief 将Flash的数据读出到将SAM的数据(Store_Data)* @param * @arg * @param * @arg * @retval None*/
void Store_Read(void){for(uint16_t i=0;i<STORE_COUNT;i++){Store_Data[i] = MyFlash_ReadHalfWord(STORE_START_ADDRESS+i*2);}
}
Store.h
#ifndef __STORE_H
#define __STORE_H
#include "stm32f10x.h" // Device headerextern uint16_t Store_Data[];
void Store_Init(void);
void Store_Write(void);
void Store_Read(void);#endif
Button.c
/*** @brief 初始化引脚PB11地开信号接收Button,用于辅助测试看门狗* @param * @arg * @param * @arg * @retval None*/
void ButtonPB11PB10_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);
}/*** @brief 获取PB11的Button是否按下* @param * @arg * @param * @arg * @retval None*/
uint8_t GetButtonPB11PB10(void){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET){Delay_ms(10);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET);Delay_ms(10);return 1;}if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == RESET){Delay_ms(10);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) == RESET);Delay_ms(10);return 2;}return 0;
}
Button.h
#ifndef __BUTTON_H
#define __BUTTON_H
#include "stm32f10x.h" // Device headeruint8_t GetButtonPB11PB10(void);
void ButtonPB11PB10_Init(void);
#endif
MyFlash.c
#include "stm32f10x.h" // Device header
/*选项字节配置可通过外部的STM32 ST-Link Utility,用户自行使用选项字节容易出现问题*//*** @brief 读取Flash指定地址的字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint32_t MyFlash_ReadWord(uint32_t Address){//对于Address,通过(__IO uint32_t *)变为uint32_t类型数据的指针,通过*取该地址的值return *((__IO uint32_t *)(Address)) ;
}/*** @brief 读取Flash指定地址的半字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint16_t MyFlash_ReadHalfWord(uint32_t Address){//对于Address,通过(__IO uint16_t *)变为uint16_t类型数据的指针,通过*取该地址的值返回return *((__IO uint16_t *)(Address)) ;//uint16_t *表示指向uint16_t数据的指针
}/*** @brief 读取Flash指定地址的字* @param Address:32位地址* @arg * @param * @arg * @retval None*/
uint8_t MyFlash_ReadByte(uint32_t Address){//对于Address,通过(__IO uint32_t *)变为uint32_t的指针,通过*取该地址的值return *((__IO uint8_t *)(Address)) ;
}/*** @brief 程序存储区flash全部擦除* @param * @arg * @param * @arg * @retval None*/
void MyFlash_EraseAll(void){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_EraseAllPages();//全部擦除FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash页擦除* @param Address:需要擦除的页地址* @arg * @param * @arg * @retval None*/
void MyFlash_ErasePage(uint32_t PageAddress){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ErasePage(PageAddress);//全部擦除FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash指定地址写字* @param * @arg * @param * @arg * @retval None*/
void MyFlash_ProgramWord(uint32_t Address,uint32_t WriteWord){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ProgramWord(Address,WriteWord);//指定地址写字FLASH_Lock();//程序存储区flash页编程关锁
}/*** @brief 程序存储区flash指定地址写半字* @param * @arg * @param * @arg * @retval None*/
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t WriteWord){FLASH_Unlock();//程序存储区flash页编程解锁FLASH_ProgramHalfWord(Address,WriteWord);//指定地址写半字,写入32bit地址的低地址,STM32flash小端存储,低字节在低位FLASH_Lock();//程序存储区flash页编程关锁
}
MyFlash.h
#ifndef __MYFLASH_H
#define __MYFLASH_H
#include "stm32f10x.h" // Device header
uint32_t MyFlash_ReadWord(uint32_t Address);
uint16_t MyFlash_ReadHalfWord(uint32_t Address);
uint8_t MyFlash_ReadByte(uint32_t Address);
void MyFlash_EraseAll(void);
void MyFlash_ErasePage(uint32_t PageAddress);
void MyFlash_ProgramWord(uint32_t Address,uint32_t WriteWord);
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t WriteWord);#endif
3、读写芯片ID
读取STM32指定地址下的原厂id号,和容量。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
int main(void){OLED_Init();//显示当前Flash最后一页的数据OLED_ShowString(1,1,"F_Size:");//OLED_ShowHexNum(1,8,*((__IO uint16_t *)(0x1FFFF7E0)),4);//我的芯片显示0x80-Flash容量128kBOLED_ShowString(2,1,"UID:");//OLED_ShowHexNum(2,5,*((__IO uint16_t *)(0x1FFFF7E8+0x02)),4);OLED_ShowHexNum(2,9,*((__IO uint16_t *)(0x1FFFF7E8)),4);OLED_ShowHexNum(3,1,*((__IO uint32_t *)(0x1FFFF7E8+0x04)),8);OLED_ShowHexNum(4,1,*((__IO uint32_t *)(0x1FFFF7E8+0x08)),8);while(1){}return 0;
}
感谢江协科技!!!
相关文章:
STM32内置Flash
一、原理 利用flash存储用户数据需要注意查看,用户数据是否会覆盖芯片运行程序。 IAP(在程序中编程)利用程序修改程序本身,和OTA是一个原理。IAP在程序中编程支持任意一种通信下载。 ICP(在电路中编程,通…...
华为路由器、交换机、AC、新版本开局远程登录那些坑(Telnet、SSH/HTTP避坑指南)
关于华为设备远程登录配置开启的通用习惯1、HTTP/HTTPS相关服务 http secure-server enablehttp server enable 2、Telnet服务telnet server enable3、SSH服务stelnet server enablessh user admin authentication-type password 「模拟器、工具合集」复制整段内容 链接&…...
【Linux】深入理解进程信号机制:信号的产生、捕获与阻塞
🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 时间不语,却回答了所有问题 目录 📚前言 📚一、信号的本质 📖1.异步通信 📖2.信…...
前端基础技术全解析:从HTML前端基础标签语言开始,逐步深入CSS样式修饰、JavaScript脚本控制、Ajax异步通信以及WebSocket持久通信
目录 前言: 1.前端技术html简单了解: 1.1HTML代码是由标签构成的。 1.2.HTML 文件基本结构 1.3.HTML 常见标签 标题标签: 段落标签: p 文本格式化标签 图片标签: 超链接标签: a 测试代码: 展示效果: 表单…...
Linux存储管理之核心秘密(The Core Secret of Linux Storage Management)
Linux存储管理之核心秘密 如果你来自Windows环境,那么Linux处理和管理存储设备的方式对你而言可能显得格外不同。我们知道,Linux的文件系统并不采用Windows那样的物理驱动器表示方式(如C:、D:或E:),而是构建了一个以&…...
excel精简使用工具
1.获取sheet1的行填充到sheet2的列 希望在 Excel 中使用 INDEX 函数从不同的列中提取数据,并且每一行都引用不同的列。为了实现这个目标,你可以使用 COLUMN 函数来动态获取列的偏移量。 为了避免手动输入每个单元格的公式,你可以使用以下公…...
Flutter鸿蒙化 在鸿蒙应用中添加Flutter页面
前言 今天这节课我们讲一下 在鸿蒙应用中添加Flutter页面。 作用: 之前有很多朋友和网友问我鸿蒙能不能使用Flutter开发,他们的项目已经用Flutter开发成熟了有什么好的方案呢,今天讲到这个就可以很好的解决他们的问题,例如我们正式项目中可能是一部分native 开发 一部分…...
为什么页面无法正确显示?都有哪些HTML和CSS相关问题?
页面无法正确显示可能由多种原因导致,通常与HTML和CSS的结构、语法错误、浏览器兼容性、资源加载等问题有关。以下是一些常见的原因及其解决方法,结合实际项目代码示例进行讲解: 1. HTML 结构错误 HTML 标签的缺失或错误可能导致页面无法正…...
如何制作一份出色的公司介绍PPT?
制作一份公司介绍的PPT需要精心设计,以确保内容既专业又吸引人。以下是一个基本的框架和一些建议,帮助您创建一份有效的公司介绍PPT: PPT标题页 标题:公司全称(可使用公司Logo作为背景或嵌入标题中)副标题…...
Selenium 进行网页自动化操作的一个示例,绕过一些网站的自动化检测。python编程
这段代码是使用 Selenium 进行网页自动化操作的一个示例,主要目的是在加载网页时执行一些自定义的 JavaScript 代码,并等待页面上某个元素的出现。以下是代码的详细解释: ### 代码解释 #### 导入必要的模块 python from selenium.webdriver…...
HashMap和HashTable的区别
1、HashMap是线程不安全的,HashTable是线程安全的 HashMap:Fail-fast 机制。表示快速失败,在集合遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常,从而导致遍历失…...
使用redis来进行调优有哪些方案?
Redis的调优方案可以从多个方面进行,以下是一些常见的优化方法及代码示例: 1.使用管道(Pipelining) 管道技术可以减少客户端与Redis之间的交互次数,从而提高性能。在批量操作时,通过管道可以一次性发送多个…...
macOS 中,默认的 Clang 编译器和 Homebrew 安装的 GCC 都不包含 bits/stdc++.h 文件
在 macOS 中,默认的 Clang 编译器和 Homebrew 安装的 GCC 都不包含 bits/stdc.h 文件,因为它是一个 非标准 的头文件,主要由 MinGW 和某些 Linux 平台的 GCC 提供。 解决方案 : 手动创建 bits/stdc.h 1. 创建文件夹和文件 在你的 GCC 标准…...
2012mfc,自绘列表控件
原文 使用常用控件版本4.70中的自定义绘画功能自定义列表控件的外观. 介绍 常见控件的4.70版引入了一项叫自定义绘画的功能. 可按轻量易用的自画版本对待自定义绘画.易用性来自,即只需处理一条消息(NM_CUSTOMDRAW),且你可让窗口为你干活,因此你不必完成物主绘画中的所有粗活…...
vue3运行时执行过程步骤
在 Vue 3 中,运行时的执行过程是一个复杂但高效的机制,主要包括初始化应用、渲染、响应式更新和销毁等阶段。以下是 Vue 3 运行时的执行过程的核心步骤和流程: 1. 应用初始化 1.1 创建 Vue 应用 调用 createApp 方法,创建一个 V…...
常用的AT命令,用于查看不同类型的网络信息
文章目录 1. ATCSQ:2. ATCREG:3. ATCOPS:4. ATCGATT:5. ATCGPADDR: 在AT命令集中,用于查看网络信息的命令有多种,具体取决于所使用的设备和模块。以下是一些常用的AT命令࿰…...
Vue3组件通讯——自定义事件(子->父)
需求如下: 1.在子组件中,当用户点击提交按钮后,更新数据库 2.数据更新成功后,子组件通知父组件getUserInfo函数,重新获取数据,同步更新 3.子组件等待getUserInfo函数执行完毕后,调用init函数…...
GLSL 着色器语言
GLSL 着色器语言 1. 着色器语言基础1.1 数据类型1.2 数据类型的基本使用1.3 运算符1.4 各个数据类型的构造函数1.5 类型转换1.6 存储限定符1.7 插值限定符1.8 一致块1.9 layout 限定符1.10 流程控制1.11 函数的声明和使用1.12 片元着色器中浮点及整型变量精度的指定1.13 程序的…...
如何创建一个 Vue.js 工程
创建一个 Vue.js 工程 可以分为以下几个步骤: 安装 Node.js 和 npm:Vue.js 依赖于 Node.js 和 npm,因此首先需要在计算机上安装 Node.js 和 npm。可以从 Node.js 的官方网站(https://nodejs.org/)下载并安装。 安装 V…...
Mysql 性能优化:覆盖索引
概述 覆盖索引(Covering Index)是一个 MySQL 查询优化技术,它指的是一个索引包含了查询所需的所有字段的数据,因此不需要回表(访问数据表的行)就可以完成查询。使用覆盖索引可以显著提高查询性能ÿ…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
