STM32标准库——(21)Flash闪存

news2025/6/19 0:50:15

1.简介

  • 第一个用途,对于我们这个C8T6芯片来说,它的程序存储器容量是64K,一般我们写个简单的程序,可能就只占前面的很小一部分空间,剩下的大片空余空间我们就可以加以利用,比如存储一些我们自定义的数据,这样就非常方便,而且可以充分利用资源,不过这里要注意我们在选取存储区域时,一定不要覆盖了原有的程序,要不然程序自己把自己给破坏了,一般存储少量的参数,我们就选最后几页存储就行了,关于如何查看程序所占用空间的大小,这个我们下小节也会介绍,然后第二个用途就是通过在程序中编程IAP,实现程序的自我更新,我们在存储用户数据时要避开程序本身,以免破坏程序,但如果我们就非要修改程序本身,这会发生什么呢,那这就是第二点提到的功能,在程序中编程,利用程序来修改程序本身,实现程序的自我更新,这个在程序中编程就是IAP。
  • 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序, ICP英文直译过来也可以叫在电路中编程,意思就是下载程序,你只需要留几个引脚就行,不用拆芯片了,就叫在电路中进行编程,ICP的作用是用于更新程序存储器的全部内容,它通过JTAG SWD协议或系统加载程序BOOTLOADER下载程序,这个JTAG SWD就是仿真器下载程序,就是我们目前用的stlink,使用SWD下载程序,每次下载都是把整个程序完全更新掉,那系统加载程序就是系统存储器的BOOTLOADER,也就是串口下载,串口下载也是更新整个程序,这就是我们一直在用的ICP下载方式,之后更高级的下载方式就是在程序中编程(In-Application Programming – IAP),简称IAP,它可以使用微控制器支持的任意一种通信接口下载程序,怎么实现呢,那比如这是整个程序存储器,我们首先需要自己写一个BOOTLOADER程序,并且存放在程序更新时不会覆盖的地方,比如我们放在最后面,然后需要更新程序时,我们控制程序跳转到这个自己写的BOOTLOADER这里来,在这里面我们就可以接收任意一种通讯接口传过来的数据,比如串口、USB、蓝牙转串口、WIFI转串口等等,这个传过来的数据就是待更新的程序,然后我们控制flash读写,把收到的程序写入到前面程序正常运行的地方,写完之后再控制程序跳转回正常运行的地方,或者直接复位,这样程序就完成了自我升级

2.闪存模块组织

  1.  对于主存储器(程序存储器 用来存放程序代码),这里对它进行了分页,分页是为了更好的管理闪存擦除和写保护都是以页为单位的,这点和之前W25Q64芯片的闪存一样,同为闪存它们的特性基本一样,写入前必须擦除,擦除必须以最小单位进行,擦除后数据位全变为1,数据只能1写0,不能0写1,擦除和写入之后都需要等待忙,这些都是一样的,学习这节之前,大家可以再复习一下W25Q64,再学这一节就会非常轻松了,那W25Q64的分配方式是先分为块block,再分为扇区sector比较复杂,这里就比较简单了,它只有一个基本单位就是页,每一页的大小都是1K,0到127总共128页,总量就是128K,对于C8T6来说,它只有64K,所以C8T6的页只有一半0~63总共64页共64K
  2. 第一个页的起始地址就程序存储器的起始地址0x08000000,之后就是一个字节一个地址依次线性分配的,看一下每页起始地址的规律,首先是0000然后0400、0800、0400,再之后1000、1400、1800,最后一直到1FC00,所以地址只要以000、400、800、400,结尾的都一定是页的起始地址,这个稍微记一下。
  3. 启动程序代码就是简介中的系统存储器 用户选择字节就是选项字节

3.基本结构

整个闪存分为程序存储器、系统存储器和选项字节三部分,这里程序存储器为以C8T6为例,它是64K的,所以总共只有64页,最后一页的起始地址是0800FC00,左边这里是闪存存储器接口,手册里还有个名称,闪存编程和擦除控制器LPEC,大家也知道这两个名称其实是一个东西就行,然后这个控制器就是闪存的管理员,他可以对程序存储器进行擦除和编程,也可以对选项字节进行擦除和编程,系统容器是不能擦除和编程的,这个选项字节里面有很大一部分配置位,其实是配置主程序存储器的读写保护的,所以右边画的写入选项字节,可以配置程序存储器的读写保护,当然选项字节还有几个别的配置参数,这个待会再讲,那这就是整个闪存的基本结构。

4.Flash解锁

首先第一步是flash解锁,这和之前W25Q64一样,W25Q64操作之前需要写使能,这个flash操作之前需要解锁,目的都是为了防止误操作,那这里解锁的方式和之前独立看门狗一样,都是通过在键寄存器写入指定的键值来实现,使用键寄存容器的好处就是更能防止误操作,每一个指令必须输密码才能完成,通过英文名称也能看出来,键的英文是KEY,直译是不是钥匙的意思,所以这个更形象的翻译我们可以把它叫做钥匙寄存器,密钥寄存器,首先LPEC共有三个键值,也就是三把开锁的钥匙,RDPRT键是解除读保护的密钥,值是0XA5,KEY1键值是0X45670123,KEY2键值是0XCDEF89AB,为什么是这些值呢,实际上是随便定义的,只要你定义的不是很简单就行,继续看怎么解锁呢,第一个是复位后FPEC被保护,不能写入FLASH_CR,也就是复位后flash默认是锁着的,然后在FLASH_KEYR键寄存器中,先写入KEY1,再写入KEY2解锁,我们找到了锁,这个锁是KEYR寄存器,怎么解呢,要先用K1钥匙解,再用K2钥匙解,最终才能解锁成功,所以这个锁的安全性非常高,有两道锁,即使程序跑飞了,歪打正着正好写入了KEY1,那也难以保证下一次又歪打正着写入了KEY2,所以非人为情况下基本不可能解锁,然后第三条还有进一步的保护措施,就是错误的操作序列会在下次复位前锁死FPEC和FLASH_CR,于是他发现有程序在尝试撬锁时,一旦没有先写入KEY1,再写入KEY2,整个模块就会完全锁死,除非复位,这是整个解锁操作,可以看到安全性非常高,接着继续看,解锁之后如何加锁呢,我们操作完成之后,要尽快把flash重新加锁,以防止意外情况,加锁的操作是设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR,这个比较简单,就是控制寄存器里面有个LOCK位,我们在这一位写1就能重新锁住闪存

4.使用指针访问存储器

  • 将0x08000000强转成__IO uint16_t *类型 即将该地址强转成uint16_t的指针类型 代表指针变量指向0x08000000的地址 此时再在外面取* 则可以得到数据
  • __IO是stm32中的宏定义 该宏定义对应C语言中的volatile 直译就是易变数据

5.程序存储器全擦除

第一步是读取lock位,看一下芯片锁没锁,下面如果lock位等于1锁住了,就执行解锁过程解锁过程就是在KEYR寄存器先写入KEY1,再写入KEY2,这里如果它当前没锁住,就不用解锁了,这是流程图里给的解锁步骤,如果锁住了就解锁,如果没锁住就不用解锁,但是在库函数中并没有这个判断,库函数是直接执行解锁过程,管你锁没锁都执行解锁,这个比较简单直接,不过效果都一样,然后继续解锁之后,首先置控制寄存器里的MER(Mass Erase)位为1,然后再置STRT(Start)位为1,其中STRT为1是触发条件,STRT为1之后芯片开始干活,然后现在看到MER位是1,它就知道接下来要干的活就是全删除,这样内部电路就会自动执行全擦除的过程,然后继续擦除也是需要花一段时间的,所以擦除过程开始后,程序要执行等待判断状态寄存器的BSY(Busy)位是否为1,BSY位表示芯片是否处于忙状态,BSY位为1表示芯片忙,所以这里如果判断BSY位等于1,就跳转回来继续循环判断,直到BSY位等于0跳出循环,最后一步这里写的是读出并验证所有页的数据,这个是测试程序才要做的,正常情况下全删除完成了,我们默认就成功了,如果还要再全读出来验证一下,这个工作量太大了,所以这里的最后一步我们就不管了,这是全擦除的流程。

6. 程序存储器页擦除

 页擦除,这个也是类似的过程,第一步一样的是解锁的流程,第二步,这个方框里的置控寄存器的PER(Page Erase)位为1,然后在AR(Address Register)地址寄存器中选择要擦除的页,最后置控制寄存器的STRT位为1,也是触发条件,芯片开始干活,然后芯片看到PER等于1,它就知道接下来要执行页擦除,然后闪存不止一页,页擦除芯片就要知道要具体擦哪一页,所以它会继续看AR寄存器的数据,AR寄存器我们要提前写入一个页的起始地址,这样芯片就会把我们指定的一页给擦除掉,然后擦除开始之后,我们也要等待BSY位,最后读出并验证数据,这个就不用看了。

7.程序存储器编程

擦除之后我们就可以执行写入的流程了,另外说明一下,STM32的闪存,在写入之前会检查指定地址有没有擦除,如果没有擦除就写入STM32则不执行写入操作,除非写入的全是0,这个数据是例外,因为不擦除就写入,可能会写入错误,但全写入0的话,写入肯定是没问题的,来看一下流程图,写入的第一步也是解锁,然后第二步我们需要置控制寄存器的PG(Programming)位为1,表示我们即将写入数据第三步就在指定的地址写入半字,这一步我们需要用到刚才说的这句代码,使用指针在指定地址写入数据,想写入什么数据,在这里指定即可,另外这里注意一下,写入操作只能以半字的形式写入,在STM32中有几个术语,字、半字和字节,其中字word就是32位数据,半字half word就是16位数据,字节byte就是8位数据,那这里只能以半字写入,意思就是只能以16位的形式写入,一次性写入两个字节,如果你要写入32位,就分两次完成,如果你只要写入八位,这个就比较麻烦了,如果你想单独写入一个字节,还要保留另一个字节的原始数据的话那只能把整页数据都读到SRAM,再随意修改SRAM数据修改全部完成之后,再把整页都擦除,最后再把整页都写回去,所以如果你想像SRAM一样随心所欲的读写,那最好的办法就先把闪存的一页读到SRAM中,读写完成后再擦除一页,整体写回去,那回到流程图这里,写入数据这个代码就触发开始的条件,不需要像擦除一样置STRT位了,写了半字之后,芯片会处于忙状态,我们等待一下BUSY清0,这样写入数据的过程就完成了,那每执行这样一个流程,只能写入一个半字,如果要写出很多数据,要不断循环调用这个流程就可以了

8.选项字节

这里是对应的16个字节,其中有一半的名称前面都带了个N,比如RDP和nRDP ,USER和nUSER等,这个意思就是你在写入RDP数据时,要同时在NRDP写入数据的反码,其他的这些都是一样,写这个存储器时,要在带N的对应的存储器写入反码,这样写入操作才是有效的,如果芯片检测到这两个存储器不是反码的关系,那就代表数据无效有错误,对应的功能就不执行,这是一个安全保障措施,但这个写入反码的过程,硬件会自动计算并写入,不需要我们操心,使用库函数的话,那就更简单了,函数都给我们分装好了,直接调用函数就行

第一个RDP(Read Protect)是读保护配置位,下面有解释,在RDP存储器写入RDPRT键,就刚才说的A5,然后解除读保护,如果RDP不是A5,那闪存就是读保护状态,无法通过调试器读取程序,避免程序被别人窃取,接着看第二个字节USER,这个是一些零碎的配置位,可以配置硬件看门狗和进入停机待机模式是否产生复位,这个了解即可,然后第三个和第四个字节data0和data1,这个在芯片中没有定义功能,用户可自定义使用,最后四个字节,WRP(Write Protect)0、1、2、3这四个字节配置的是写保护在中容量产品里是每一个位对应保护四个存储页,四个字节(1个字节有8位)总共32位,一位对应保护四页,总共保护32×4等于128页,正好对应中容量量的最大128页,那对于小容量和大容量产品呢,可以看一下手册,2.5选项字节说明这里,对于小容量产品,也是每一位对应保护四个存储页,但小容量产品最大只有32K,所以只需要一个字节WRP0就行,4×8=32,其他三个字节没用到,然而对于大容量产品,每一个位只能保护两个存储页,这样的话四个字节就不够用了,所以这里规定WRP3的最高位,这一位直接把剩下的所有页一起都保护了,这是写保护的定义。

9.选项字节擦除

第一步其实也是解锁闪存,这里文字并没有写,然后第二步这里文字版的流程多了一步,检查SR的BSY位,以确认没有其他正在进行的闪存操作,这个实际上就是事前等待,如果当前已经在忙了,我先等一下,这一步在刚才的流程图里并没有体现,然后下一步解锁CR的OPTWRE(Option Write Enable)位,这一步是选项字节的解锁选项字节里面还有一个单独的锁,在解锁闪存后,还需要再解锁选项字节的锁,之后才能操作选项字节,解锁选项字节的话看一下前面的寄存器(前面闪存模块组织图),整个闪存的锁是KEYR,里面选项字节的小锁是下面的OPTKEYR(Option Key Register),解锁这个小锁也是类似的流程,我们需要在OPTKEYR里先写入KEY1,再写入KEY2,这样就能解锁选项字节的小锁了,然后继续解除小锁之后和之前的擦除类似,先设置CR的OPTER(Option Erase)位为1,表示即将擦除选项字节,之后设置CR的STRT位为1,触发芯片开始干活,这样芯片就会启动擦除选项字节的工作之后等待BUSY位变为0,擦除选项字节就完成了,擦除之后就可以看写入了。

10.选项字节写入

11.器件电子签名

使用指针读指定地址下的存储器,可获取电子签名,电子签名其实就是STM32的id号它的存放区域是系统存储器,它不仅有BOOTLOADER程序,还有几个字节的id号,系统存储器起始地址是1FFFF000,看下这里,这里有两段数据,第一个是闪存容量存储器,基地址是1FFF F7E0,通过地址也可以确定它的位置,就是系统存储器,这个存储器的大小是16位,它的值就是闪存的容量单位是KB,然后第二个是产品唯一身份标识寄存器,就是每个芯片的身份证号,这个数据存放的基地址是1FFFF7E8,大小是96位,每一个芯片的这96位数据都是不一样的,使用这个唯一id号可以做一些加密的操作,比如你想写入一段程序,只能在指定设备运行,那也可以在程序的多处加入id号判断,如果不是指定设备的id号,就不执行程序功能,这样即使你的程序被盗,在别的设备上也难以运行,这是STM32的电子签名。

12.读写内部Flash

12.1 接线图

12.2 相关代码

12.2.1 MyFLASH.c
#include "stm32f10x.h"                  // Device header

/**
  * 函    数:FLASH读取一个32位的字
  * 参    数:Address 要读取数据的字地址
  * 返 回 值:指定地址下的数据
  */
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
	return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个16位的半字
  * 参    数:Address 要读取数据的半字地址
  * 返 回 值:指定地址下的数据
  */
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
	return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个8位的字节
  * 参    数:Address 要读取数据的字节地址
  * 返 回 值:指定地址下的数据
  */
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
	return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH全擦除
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在
  */
void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();					//解锁
	FLASH_EraseAllPages();			//全擦除
	FLASH_Lock();					//加锁
}

/**
  * 函    数:FLASH页擦除
  * 参    数:PageAddress 要擦除页的页地址
  * 返 回 值:无
  */
void MyFLASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();					//解锁
	FLASH_ErasePage(PageAddress);	//页擦除
	FLASH_Lock();					//加锁
}

/**
  * 函    数:FLASH编程字
  * 参    数:Address 要写入数据的字地址
  * 参    数:Data 要写入的32位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
	FLASH_Unlock();							//解锁
	FLASH_ProgramWord(Address, Data);		//编程字
	FLASH_Lock();							//加锁
}

/**
  * 函    数:FLASH编程半字
  * 参    数:Address 要写入数据的半字地址
  * 参    数:Data 要写入的16位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
	FLASH_Unlock();							//解锁
	FLASH_ProgramHalfWord(Address, Data);	//编程半字
	FLASH_Lock();							//加锁
}
12.2.2 MyFLASH.h
#ifndef __MYFLASH_H
#define __MYFLASH_H

uint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);

void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);

void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

#endif
12.2.3 Store.c
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS		0x0800FC00		//存储的起始地址
#define STORE_COUNT				512				//存储数据的个数

uint16_t Store_Data[STORE_COUNT];				//定义SRAM数组

/**
  * 函    数:参数存储模块初始化
  * 参    数:无
  * 返 回 值:无
  */
void Store_Init(void)
{
	/*判断是不是第一次使用*/
	if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)	//读取第一个半字的标志位,if成立,则执行第一次使用的初始化
	{
		MyFLASH_ErasePage(STORE_START_ADDRESS);					//擦除指定页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);	//在第一个半字写入自己规定的标志位,用于判断是不是第一次使用
		for (uint16_t i = 1; i < STORE_COUNT; i ++)				//循环STORE_COUNT次,除了第一个标志位
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);		//除了标志位的有效数据全部清0
		}
	}
	
	/*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/
	for (uint16_t i = 0; i < STORE_COUNT; i ++)					//循环STORE_COUNT次,包括第一个标志位
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);		//将闪存的数据加载回SRAM数组
	}
}

/**
  * 函    数:参数存储模块保存数据到闪存
  * 参    数:无
  * 返 回 值:无
  */
void Store_Save(void)
{
	MyFLASH_ErasePage(STORE_START_ADDRESS);				//擦除指定页
	for (uint16_t i = 0; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,包括第一个标志位
	{
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);	//将SRAM数组的数据备份保存到闪存
	}
}

/**
  * 函    数:参数存储模块将所有有效数据清0
  * 参    数:无
  * 返 回 值:无
  */
void Store_Clear(void)
{
	for (uint16_t i = 1; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,除了第一个标志位
	{
		Store_Data[i] = 0x0000;							//SRAM数组有效数据清0
	}
	Store_Save();										//保存数据到闪存
}
12.2.4 Strore.h
#ifndef __STORE_H
#define __STORE_H

extern uint16_t Store_Data[];

void Store_Init(void);
void Store_Save(void);
void Store_Clear(void);

#endif
12.2.5 main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"

uint8_t KeyNum;					//定义用于接收按键键码的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	Key_Init();					//按键初始化
	Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");
	
	while (1)
	{
		KeyNum = Key_GetNum();		//获取按键键码
		
		if (KeyNum == 1)			//按键1按下
		{
			Store_Data[1] ++;		//变换测试数据
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失
		}
		
		if (KeyNum == 2)			//按键2按下
		{
			Store_Clear();			//将Store_Data的数据全部清0
		}
		
		OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位
		OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据
		OLED_ShowHexNum(3, 6, Store_Data[2], 4);
		OLED_ShowHexNum(4, 1, Store_Data[3], 4);
		OLED_ShowHexNum(4, 6, Store_Data[4], 4);
	}
}

现象:Flag显示闪存的第一个标志位 Data中的数据 每按下一次按键 第一个数据自增1 第二个数据自增2 依次类推 自增后调用Store_Save 将数据全部存储到闪存中 于是每次上电后 函数初始化会将闪存中的数据读取到数组中并显示在OLED屏 实现掉电不丢失

13.读取芯片ID

13.1 接线图

13.2 相关代码

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

int main(void)
{
	OLED_Init();						//OLED初始化
	
	OLED_ShowString(1, 1, "F_SIZE:");	//显示静态字符串
	OLED_ShowHexNum(1, 8, *((__IO uint16_t *)(0x1FFFF7E0)), 4);		//使用指针读取指定地址下的闪存容量寄存器
	
	OLED_ShowString(2, 1, "U_ID:");		//显示静态字符串
	OLED_ShowHexNum(2, 6, *((__IO uint16_t *)(0x1FFFF7E8)), 4);		//使用指针读取指定地址下的产品唯一身份标识寄存器
	OLED_ShowHexNum(2, 11, *((__IO uint16_t *)(0x1FFFF7E8 + 0x02)), 4);
	OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x04)), 8);
	OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x08)), 8);
	
	while (1)
	{
		
	}
}

 

现象:显示屏第二行以16位半字显示地址 低端先行 所以从左到右是001E 002C 第三行以32位显示则为30313432 第四行也是32位 即42313130

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

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

相关文章

单数码管(arduino)

1.连接方法 挨个点亮每个灯 #include <Arduino.h>int pin_list[] {4, 5, 19, 21, 22, 2, 15, 18}; int num_pins sizeof(pin_list) / sizeof(pin_list[0]); // 计算数组中的元素数量void setup() {// 设置每个引脚为输出for(int i 0; i < num_pins; i) {pinMode(p…

Synthetic Temporal Anomaly Guided End-to-End Video Anomaly Detection 论文阅读

Synthetic Temporal Anomaly Guided End-to-End Video Anomaly Detection 论文阅读 Abstract1. Introduction2. Related Work3. Methodology3.1. Architecture3.1.1 Autoencoder3.1.2 Temporal Pseudo Anomaly Synthesizer 3.2. Training3.3. Anomaly Score 4. Experiments4.1.…

详解Linux例行性工作

例行性工作&#xff08;计划任务&#xff09; 场景&#xff1a; 生活中&#xff0c;我们有太多场景需要使用到闹钟&#xff0c;比如早上7点起床&#xff0c;下午4点开会&#xff0c;晚上8点购物&#xff0c;等等。再Linux系统里&#xff0c;我们同样也有类似的需求。比如我们…

C语言学习--练习3(贪心)

目录 贪心算法 1. 两数对之间的最大乘积差 2.三角形的最大周长 3.数组拆分 4.救生艇 5.发送饼干 6.摆动数组 贪心算法 概念定义 所谓贪心&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上进行考虑&#xff0c;算法得到的是在某种…

第三百九十一回

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何混合选择多个图片和视频文件"相关的内容&#xff0c;本章回中将介绍如何通过相机获取视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. …

【数据库-黑马笔记】基础-SQL

本文参考b站黑马数据库视频,总结详细全面的笔记 ,可结合视频观看1~26集 MYSQL 的基础知识框架如下 目录 一、MYSQL概述 1、数据库相关概念 2、MYSQL的安装及启动 二、SQL 1、DDL【Data Defination】 2、DML【Data Manipulation】 ①、插入 ②、更新和删除 3、 DQL【Data…

【Scrapy】京东商品数据可视化

【Scrapy】京东商品数据可视化 文章目录 【Scrapy】京东商品数据可视化  &#x1f449;引言&#x1f48e;一、爬取数据&#xff1a;1.1 scrapy爬虫库简介&#xff1a;1.2 技术实现&#xff1a;1.2.1搭建框架结构1.2.2 分析网页结构 二、数据保存&#xff1a;三、数据读取以及…

【Algorithms 4】算法(第4版)学习笔记 16 - 4.2 有向图

文章目录 前言参考目录学习笔记1&#xff1a;介绍1.1&#xff1a;有向图简介1.2&#xff1a;应用举例1.3&#xff1a;相关问题2&#xff1a;有向图 API2.1&#xff1a;有向图表示2.1.1&#xff1a;邻接表数组 Adjacency-list2.1.2&#xff1a;Java 实现&#xff1a;邻接表数组2…

2024年k8s最新版本安装教程

k8s安装教程 1 k8s介绍2 环境搭建2.1 主机准备2.2 主机初始化2.2.1 安装wget2.2.2 更换yum源2.2.3 常用软件安装2.2.4 关闭防火墙2.2.5 关闭selinux2.2.6 关闭 swap2.2.7 同步时间2.2.8 修改Linux内核参数2.2.9 配置ipvs功能 2.3 容器安装2.3.1 设置软件yum源2.3.2 安装docker软…

C# OpenVINO Yolov8-OBB 旋转目标检测

目录 效果 模型 项目 代码 下载 C# OpenVINO Yolov8-OBB 旋转目标检测 效果 模型 Model Properties ------------------------- date&#xff1a;2024-02-26T08:38:44.171849 description&#xff1a;Ultralytics YOLOv8s-obb model trained on runs/DOTAv1.0-ms.yaml …

论文的引用书写方法

前置操作 1、全选文献 2、在开始选项卡 段落功能区 选择编号功能 3、设置编号格式 [1] 论文的引用 1、光标放在需要引用论文的地方 2、选择引用选项卡 点击交叉引用 3、引用类型为编号项 引用内容为段落编号 选择需要的第几条参考文献

Linux第74步_“设备树”下的LED驱动

使用新字符设备驱动的一般模板&#xff0c;以及设备树&#xff0c;驱动LED。 1、添加“stm32mp1_led”节点 打开虚拟机上“VSCode”&#xff0c;点击“文件”&#xff0c;点击“打开文件夹”&#xff0c;点击“zgq”&#xff0c;点击“linux”&#xff0c;点击“atk-mp1”&am…

数据库的筛选条件

【一】筛选过滤条件 【1】完整的查询语句 -- 查询当前表中的全部数据select * from 表名 where 筛选条件;​-- 查询当前表中的指定字段的数据select 字段名,字段名 from 表名 where 筛选条件;# 执行顺序from where select ​select 你选择的列1, 你选择的列2, ... from 查询的…

UVA191 Intersection 题解

UVA191 Intersection 题解 水紫&#xff0c;建议降蓝。 解法 求线段相交裸题&#xff0c;考虑使用向量解决。 我们把矩形的 4 4 4 个点两两相连&#xff0c;得到 6 6 6 条线段&#xff0c;如果先输入的线段和这 6 6 6 条线段都没有交点&#xff0c;则认为线段和矩形不相…

高度塌陷问题及解决

什么情况下产生 (when 父盒子没有定义高度&#xff0c;但是子元素有高度&#xff0c;希望用子盒子撑起父盒子的高度&#xff0c;但是子盒子添加了浮动属性之后&#xff0c;父盒子高度为0 <template><div class"father"><div class"son"&…

基于命名实体链接的事件抽取与知识图谱在电商领域的应用

开源项目推荐 多模态AI能力引擎平台: 免费的自然语言处理、情感分析、实体识别、图像识别与分类、OCR识别、语音识别接口&#xff0c;功能强大&#xff0c;欢迎体验。 多模态AI能力引擎平台: 免费的自然语言处理、情感分析、实体识别、图像识别与分类、OCR识别、语音识别接口…

代码还原之 函数

指令堆里逆向出来的代码有歧义&#xff0c;有三处返回&#xff0c;有嵌套IF语句&#xff0c;故推断出是个函数&#xff1b; #if 0/*27ec: 48 8d 3d 58 39 00 00 lea 0x3958(%rip),%rdi # 614b <_IO_stdin_usedBase0x14b> // rdi"COLUMNS"27f3: e8 e…

Express学习(三)

Express中间件 中间件的概念 什么是中间件 中间件&#xff0c;特指业务流程的中间处理环节。Express中间件的调用流程 当一个请求到达Express的服务器之后&#xff0c;可以连续调用多个中间件&#xff0c;从而对这次请求进行预处理。类似于下图所示 Express中间件的格式 Expr…

安卓SDK dx工具生成dex文件命令

目录 前言一、添加到环境变量二、命令示例 前言 在Android SDK 工具中&#xff0c;dx命令用于将Java字节码文件转换为Dalvik Executable&#xff08;dex&#xff09;文件&#xff0c;以便在Android设备上运行。 一、添加到环境变量 找到想要使用的SDK版本&#xff0c;将dx.b…

C++11 新特性 增加数据类型

一.C11 新增加数据类型介绍 在C11中&#xff0c;新增了long long、unsigned long long、char16_t和char32_t等数据类型&#xff0c;它们可以支持更宽的整型和字符表示。 long long&#xff1a;long long是一种整数类型&#xff0c;它至少可以存储64位&#xff08;8字节&#x…