STM32保护内部FLASH

 在实际发布的产品中,在STM32芯片的内部FLASH存储了控制程序,如果不作任何保护措施的话,可以使用下载器直接把内部FLASH的内容读取回来,得到bin或hex文件格式的代码拷贝,别有用心的厂商即可利用该代码文件山寨产品。为此,STM32芯片提供了多种方式保护内部FLASH的程序不被非法读取,但在默认情况下该保护功能是不开启的,若要开启该功能,需要改写内部FLASH选项字节(Option Bytes)中的配置。

11 选项字节与读写保护

选项字节是一段特殊的FLASH空间,STM32芯片会根据它的内容进行读写保护等配置,选项字节的构成如下:

        STM32F103系列芯片的选项字节有8个配置项,即上表中的USER、RDP、DATA0/1及WRP0/1/2/3,而表中带n的同类项是该项的反码,即nUSER的值等于(~USER)、nRDP的值等于(~RDP),STM32利用反码来确保选项字节内容的正确性。

选项字节的内容:

数据位配置(第一部分):

 

数据位配置(第二部分): 

其中的RDP位和WRP位,它们分别用于配置读保护和写保护

RDP读保护级别

修改选项字节的RDP位的值可设置内部FLASH为以下保护级别:

  • 0xA5:级别0,无保护     这是STM32的默认保护级别,它没有任何读保护,读取内部FLASH的内容都没有任何限制。也就是说,第三方可以使用调试器等工具,获取该芯片FLASH中存储的程序,然后可以把获得的程序以bin和hex的格式下载到另一块STM32芯片中,加上PCB抄板技术,轻易复制出同样的产品。
  • 其它值:级别1,使能读保护     把RDP配置成除0xA5外的任意数值, 都会使能读保护。在这种情况下,若使用调试功能(使用下载器、仿真器)或者从内部SRAM自举时都不能对内部FLASH作任何访问(读写、擦除都被禁止);而如果STM32是从内部FLASH自举时,它允许对内部FLASH的任意访问。也就是说,任何尝试从外部访问内部FLASH内容的操作都被禁止。

例如,无法通过下载器读取它的内容,或编写一个从内部SRAM启动的程序,若该SRAM启动的程序读取内部FLASH,会被禁止。而如果是芯片原本的内部FLASH程序自己访问内部FLASH(即从FLASH自举的程序),是完全没有问题的,例如芯片本身的程序,若包含有指针对内部FLASH某个地址进行的读取操作,它能获取正常的数据。

        另外,被设置成读保护后,FLASH前4K字节的空间会强制加上写保护,也就是说,即使是从FLASH启动的程序,也无法擦写这4K字节空间的内容;而对于前4K字节以外的空间,读保护并不影响它对其它空间的擦除/写入操作。利用这个特性,可以编写IAP代码(In Application Program)更新FLASH中的程序,它的原理是通过某个通讯接口获取将要更新的程序内容,然后利用内部FLASH擦写操作把这些内容烧录到自己的内部FLASH中,实现应用程序的更新,该原理类似串口ISP程序下载功能,只不过ISP这个接收数据并更新的代码由ST提供,且存放在系统存储区域,而IAP是由用户自行编写的,存放在用户自定义的FLASH区域,且通讯方式可根据用户自身的需求定制,如IIC、SPI等,只要能接收到数据均可。

解除保护       

 当需要解除芯片的读保护时,要把选项字节的RDP位重新设置为0xA5。在解除保护前,芯片会自动触发擦除主FLASH存储器的全部内容,即解除保护后原内部FLASH的代码会丢失,从而防止降级后原内容被读取到。

芯片被配置成读保护后根据不同的使用情况,访问权限不同,总结如下表:

WRP写保护

使用选项字节的WRP0/1/2/3可以设置主FLASH的写保护,防止它存储的程序内容被修改。

  • 设置写保护
            写保护的配置一般以4K字节为单位,除WRP3的最后一位比较特殊外,每个WRP选项字节的一位用于控制4K字节的写访问权限, 把对应WRP的位置0即可把它匹配的空间加入写保护。被设置成写保护后,主FLASH中的内容使用任何方式都不能被擦除和写入,写保护不会影响读访问权限,读访问权限完全由前面介绍的读保护设置限制。
  • 解除写保护
            解除写保护是逆过程,把对应WRP的位置1即可把它匹配的空间解除写保护。解除写保护后,主FLASH中的内容不会像解读保护那样丢失,它会被原样保留。

12 修改选项字节的过程

根据前面的说明,修改选项字节的内容可修改读写保护配置,不过选项字节复位后的默认状态是始终可以读但被写保护的,因此它具有类似前面《读写内部FLASH》章节提到的FLASH_CR寄存器的访问限制,要想修改,需要先对FLASH_OPTKEYR寄存器写入解锁编码。由于修改选项字节时也需要访问FLASH_CR寄存器,所以同样也要对FLASH_KEYR写入解锁编码。

修改选项字节的配置步骤如下:

  • 解除FLASH_CR寄存器的访问限制
  • 往FPEC键寄存器 FLASH_KEYR中写入 KEY1 = 0x45670123
  • 再往FPEC键寄存器 FLASH_KEYR中写入 KEY2 = 0xCDEF89AB
  • 解除对选项字节的访问限制
  • 往FLASH_OPTKEYR中写入 KEY1 = 0x45670123
  • 再往FLASH_OPTKEYR中写入 KEY2 = 0xCDEF89AB
  • 配置FLASH_CR的OPTPG位,准备修改选项字节
  • 直接使用指针操作修改选项字节的内容,根据需要修改RDP、WRP等内容
  • 对于读保护的解除,由于它会擦除FLASH的内容,所以需要检测状态寄存器标志位以确认FLASH擦除操作完成。
  • 若是设置读保护及其解除,需要给芯片重新上电复位,以使新配置的选项字节生效;对于设置写保护及其解除,需要给芯片进行系统复位,以使新配置的选项字节生效。

13 操作选项字节的库函数

1.选项字结构体定义

标准库中定义的选项字节结构体,包含了RDP、USER、DATA0/1及WRP0/1/2/3这些内容,每个结构体成员指向选项字节对应选项的原始配置码及反码。不过,根据手册中的说明可了解到,当向选项字节的这些地址写入配置时,它会自动取低位字节计算出高位字节的值再存储,即自动取反码,非常方便。例如程序中执行操作给结构体成员WRP0赋值为0x0011时,最终它会自动写入0xEE11(0xEE是0x11的反码)。最后,从OB_BASE宏的定义可以确认它所指向的正是前面介绍的选项字节基地址,说明若在程序中使用该结构体赋值,会直接把内容写入到选项字节地址对应的空间中。

1 /**
2 * @brief 选项字节结构体
3 */
4 typedef struct {
5 __IO uint16_t RDP; /*RDP 及 nRDP*/
6 __IO uint16_t USER; /*USER 及 nUSER, 下面类似 */
7 __IO uint16_t Data0;
8 __IO uint16_t Data1;
9 __IO uint16_t WRP0;
10 __IO uint16_t WRP1;
11 __IO uint16_t WRP2;
12 __IO uint16_t WRP3;
13 } OB_TypeDef;
14
15 /* 强制转换为选项字节结构体指针 */
16 #define OB ((OB_TypeDef *) OB_BASE)
17 /* 选项字节基地址 */
18 #define OB_BASE ((uint32_t)0x1FFFF800)

库文件提供了 FLASH_EnableWriteProtection 函数,可用于设置写保护及解除:

1 #define RDP_Key ((uint16_t)0x00A5)
2
3 /**
4 * @brief 使能或关闭读保护
5 * @note 若芯片本身有对选项字节进行其它操作,
6 请先读出然后再重新写入,因为本函数会擦除所有选项字节的内容
7
8 * @param Newstate: 使能(ENABLE)或关闭(DISABLE)
9 * @retval FLASH Status: 可能的返回值: FLASH_ERROR_PG,
10 * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
11 */
12 FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)
13 {
14 FLASH_Status status = FLASH_COMPLETE;
15 /* 检查参数 */
16 assert_param(IS_FUNCTIONAL_STATE(NewState));
17 status = FLASH_WaitForLastOperation(EraseTimeout);
18 if (status == FLASH_COMPLETE) {
19 /* 写入选项字节解锁码 */
20 FLASH->OPTKEYR = FLASH_KEY1;
21 FLASH->OPTKEYR = FLASH_KEY2;
22 FLASH->CR |= CR_OPTER_Set; //擦除选项字节
23 FLASH->CR |= CR_STRT_Set; //开始擦除
24 /* 等待上一次操作完毕 */
25 status = FLASH_WaitForLastOperation(EraseTimeout);
26 if (status == FLASH_COMPLETE) {
27 /* 若擦除操作完成,复位 OPTER 位 */
28 FLASH->CR &= CR_OPTER_Reset;
29 /* 准备写入选项字节 */
30 FLASH->CR |= CR_OPTPG_Set;
31 if (NewState != DISABLE) {
32 OB->RDP = 0x00;//写入非 0xA5 值,进行读保护
33 } else {
34 OB->RDP = RDP_Key; //写入 0xA5,解除读保护
35 }
36 /* 等待上一次操作完毕 */
37 status = FLASH_WaitForLastOperation(EraseTimeout);
38
39 if (status != FLASH_TIMEOUT) {
40 /* 若操作完毕,复位 OPTPG 位 */
41 FLASH->CR &= CR_OPTPG_Reset;
42 }
43 } else {
44 if (status != FLASH_TIMEOUT) {
45 /* 复位 OPTER 位 */
46 FLASH->CR &= CR_OPTER_Reset;
47 }
48 }
49 }
50 /* 返回设置结果 */
51 return status;
52 }

        该函数的输入参数可选FLASH_WRProt_Pages0to1至FLASH_WRProt_Pages62to511等宏,该参数用于指定要对哪些页进行写保护。
        从该宏的定义方式可了解到,它用一个32位的数值表示WRP0/1/2/3,而宏名中的页码使用数据位1来在WRP0/1/2/3中对应的位作掩码指示。如控制页0至页1的宏FLASH_WRProt_Pages0to1,它由WRP0最低位控制,所以其宏值为0x00000001(bit0为1);类似地,控制页2至页3的宏FLASH_WRProt_Pages2to3,由WRP0的bit1控制,所以其宏值为0x00000002(bit1为1)。  
          理解了输入参数宏的结构后,即可分析函数中的具体代码。其中最核心要理解的是对输入参数的运算,输入参数FLASH_Pages自身会进行取反操作,从而用于指示要保护页的宏对应的数据位会被置0,而在选项字节WRP中,被写0的数据位对应的页会被保护。FLASH_Pages取反后的值被分解成WRP0/1/2/3_Data四个部分,所以在后面的代码中,可以直接把WRP0/1/2/3_Data变量的值写入到选项字节中。关于这部分运算,您可以亲自代入几个宏进行运算,加深理解。
        得到数据后,函数开始对FLASH_OPTKEYR寄存器写入解锁码,然后操作FLASH_CR寄存器的OPTPG位准备写入,写入的时候它直接往指向选项字节的结构体OB赋值,如OB->WRP0 = WRP0_Data,注意在这部分写入的时候,根据前面的运算,可知WRP0_Data中只包含了WRP0的内容,而nWRP0的值为0,这个nWRP0的值最终会由芯片自动产生。代码后面的WRP1/2/3操作类似。   
         仔细研究了这个库函数后,可知它内部并没有对FLASH_CR的访问作解锁操作,所以在调用本函数前,需要先调用FLASH_Unlock解锁。另外,库文件中并没有直接的函数用于解除保护,但实际上解除保护也可以使用这个函数来处理,例如使用输入参数0来调用函数FLASH_EnableWriteProtection(0),根据代码的处理,它最终会向WRP0/1/2/3选项字节全写入1,从而达到整片FLASH解除写保护的目的。

2.设置写保护及解除

由于读保护都是针对整个芯片的,所以读保护的配置函数相对简单,它通过输入参数ENABLE或DISABL参数来进行保护或解除。它的内部处理与前面介绍的修改选项字节过程完全一致,当要进行读保护时,往选项字节结构体OB->RDP写入0x00(实际上写入非0xA5的值均可达到目的),而要解除读保护时,则写入0xA5。 要注意的是,本函数同样有对FLASH_CR寄存器的访问,但并没有进行解锁操作,所以调用本函数前,同样需要先使用FLASH_Unlock函数解锁。

1 #define RDP_Key ((uint16_t)0x00A5)
2
3
4 /**
5 * @brief 使能或关闭读保护
6 * @note 若芯片本身有对选项字节进行其它操作,
7 请先读出然后再重新写入,因为本函数会擦除所有选项字节的内容
8
9 * @param Newstate: 使能(ENABLE)或关闭(DISABLE)
10 * @retval FLASH Status: 可能的返回值: FLASH_ERROR_PG,
11 * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
12 */
13 FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)
14 {
15 FLASH_Status status = FLASH_COMPLETE;
16 /* 检查参数 */
17 assert_param(IS_FUNCTIONAL_STATE(NewState));
18 status = FLASH_WaitForLastOperation(EraseTimeout);
19 if (status == FLASH_COMPLETE) {
20 /* 写入选项字节解锁码 */
21 FLASH->OPTKEYR = FLASH_KEY1;
22 FLASH->OPTKEYR = FLASH_KEY2;
23 FLASH->CR |= CR_OPTER_Set; //擦除选项字节
24 FLASH->CR |= CR_STRT_Set; //开始擦除
25 /* 等待上一次操作完毕 */
26 status = FLASH_WaitForLastOperation(EraseTimeout);
27 if (status == FLASH_COMPLETE) {
28 /* 若擦除操作完成,复位 OPTER 位 */
29 FLASH->CR &= CR_OPTER_Reset;
30 /* 准备写入选项字节 */
31 FLASH->CR |= CR_OPTPG_Set;
32 if (NewState != DISABLE) {
33 OB->RDP = 0x00;//写入非 0xA5 值,进行读保护
34 } else {
35 OB->RDP = RDP_Key; //写入 0xA5,解除读保护
36 }
37 /* 等待上一次操作完毕 */
38 status = FLASH_WaitForLastOperation(EraseTimeout);
39
40 if (status != FLASH_TIMEOUT) {
41 /* 若操作完毕,复位 OPTPG 位 */
42 FLASH->CR &= CR_OPTPG_Reset;
43 }
44 } else {
45 if (status != FLASH_TIMEOUT) {
46 /* 复位 OPTER 位 */
47 FLASH->CR &= CR_OPTER_Reset;
48 }
49 }
50 }
51 /* 返回设置结果 */
52 return status;
53 }

要注意的是,本函数同样有对 FLASH_CR 寄存器的访问,但并没有进行解锁操作,所以调用本 函数前,同样需要先使用 FLASH_Unlock 函数解锁。

14 实验:设置读写保护及解除

本实验要进行的操作比较特殊,由于设置成读写保护状态后,若不解除保护状态或者解除代码

工作不正常,将无法给芯片的 FLASH 下载新的程序,所以本程序在开发过程中使用内部 SRAM

调试的方式开发,便于测试程序(读写保护只影响 FLASH,SRAM 调试时程序下载到 SRAM 中,

不受影响)。工程中,提供了 FLASH 和 SRAM 调试的版本,见图两种版本的程序 

工程的 FLASH 版本程序包含完整的保护及解除方案,程序下载到内部 FLASH 后,它自身可以正

常地进行保护及解除。另外,在学习过程中如果您想亲自修改该代码进行测试,也不用担心把解

除操作的代码修改至工作不正常而导致芯片无法解锁报废,处于这种情况时,只要使用本工程的

SRAM 版本下载到芯片中,即可实现解锁。只要具备前面章节介绍的 SRAM 调试知识并备份了

SRAM 版本的工程即可大胆尝试。

bsp_readWriteProtect.c


#include "./protect/bsp_readWriteProtect.h"   
#include "./usart/bsp_usart.h"/*** @brief  反转写保护的配置,用于演示若芯片处于写保护状态,则解除,若不是写保护状态,则设置成写保护* @param  无* @retval 无*/
void WriteProtect_Toggle(void)
{/* 获取写保护寄存器的值进行判断,寄存器位为0表示有保护,为1表示无保护 *//*  若不等于0xFFFFFFFF,则说明有部分页被写保护了 */if(FLASH_GetWriteProtectionOptionByte() != 0xFFFFFFFF ){FLASH_DEBUG("芯片处于写保护状态,即将执行解保护过程...");//解除对FLASH_CR寄存器的访问限制FLASH_Unlock();/* 擦除所有选项字节的内容 */FLASH_EraseOptionBytes();/* 对所有页解除 */FLASH_EnableWriteProtection(0x00000000);FLASH_DEBUG("配置完成,芯片将自动复位加载新配置,复位后芯片会解除写保护状态\r\n");/* 复位芯片,以使选项字节生效 */NVIC_SystemReset();}else //无写保护{FLASH_DEBUG("芯片处于无写保护状态,即将执行写保护过程...");//解除对FLASH_CR寄存器的访问限制FLASH_Unlock();/* 先擦除所有选项字节的内容,防止因为原有的写保护导致无法写入新的保护配置 */FLASH_EraseOptionBytes();/* 对所有页进行写保护 */FLASH_EnableWriteProtection(FLASH_WRProt_AllPages);FLASH_DEBUG("配置完成,芯片将自动复位加载新配置,复位后芯片会处于写保护状态\r\n");/* 复位芯片,以使选项字节生效 */NVIC_SystemReset();		}}/*** @brief  反转读保护的配置,用于演示若芯片处于读保护状态,则解除,若不是读保护状态,则设置成读保护* @param  无* @retval 无*/
void ReadProtect_Toggle(void)
{if(FLASH_GetReadOutProtectionStatus () == SET ){FLASH_DEBUG("芯片处于读保护状态\r\n");//解除对FLASH_CR寄存器的访问限制FLASH_Unlock();FLASH_DEBUG("即将解除读保护,解除读保护会把FLASH的所有内容清空");FLASH_DEBUG("由于解除后程序被清空,所以后面不会有任何提示输出");FLASH_DEBUG("等待20秒后即可给芯片下载新的程序...\r\n");FLASH_ReadOutProtection (DISABLE);		//即使在此处加入printf串口调试也不会执行的,因为存储程序的整片FLASH都已被擦除。FLASH_DEBUG("由于FLASH程序被清空,所以本代码不会被执行,串口不会有本语句输出(SRAM调试模式下例外)\r\n");}else{FLASH_DEBUG("芯片处于无读保护状态,即将执行读保护过程...\r\n");//解除对FLASH_CR寄存器的访问限制FLASH_Unlock();				FLASH_ReadOutProtection (ENABLE);printf("芯片已被设置为读保护,上电复位后生效(必须重新给开发板上电,只按复位键无效)\r\n");printf("处于保护状态下无法正常下载新程序,必须要先解除保护状态再下载\r\n");}
}

bsp_readWriteProtect.h

#ifndef __INTERNAL_FLASH_H
#define	__INTERNAL_FLASH_H#include "stm32f10x.h"/* STM32大容量产品每页大小2KByte,中、小容量产品每页大小1KByte */
#if defined (STM32F10X_HD) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) || defined (STM32F10X_XL)#define FLASH_PAGE_SIZE    ((uint16_t)0x800)	//2048
#else#define FLASH_PAGE_SIZE    ((uint16_t)0x400)	//1024
#endif//写入的起始地址与结束地址
#define WRITE_START_ADDR  ((uint32_t)0x08008000)
#define WRITE_END_ADDR    ((uint32_t)0x0800C000)typedef enum 
{WRITE_PROTECTED = 0, NO_WRITE_PROTECT 
} Protect_Status;/*********************************************/
/*信息输出*/
#define FLASH_DEBUG_ON         1#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\if(FLASH_DEBUG_ON)\printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\}while(0)Protect_Status WriteProtect_Status(void);
void WriteProtect_Toggle(void);																				
void ReadProtect_Toggle(void);#endif /* __INTERNAL_FLASH_H */

main.c

//【 !!】注意事项:
//1.当芯片处于读写保护状态时,均无法下载新的程序,需要先解除保护状态后再下载
//2.本工程包含两个版本,可在MDK的“Load”下载按钮旁边的下拉框选择:
//	FLASH版本:	接上串口调试助手后,直接点击MDK的“Load”按钮把程序下载到STM32的FLASH中,
//				复位运行,串口会输出当前芯片的保护状态,可使用KEY1和KEY2切换。切换写保护
//				状态时,芯片会自动复位,程序重新执行;切换读保护状态时,按键后需要重新给
//				开发板上电复位,配置才会有效(断电时,串口与电脑的连接会断开,所以上电后
//				注意重新打开串口调试助手),若是执行解除读保护过程,运行后芯片FLASH中自身
//				的代码都会消失,所以要重新给开发板下载程序。
//	RAM版本  :	若无SRAM调试程序的经验,请先学习前面的《SRAM调试》章节。接上串口调试助手后,
//				只能使用MDK的“Debug”按钮把程序下载到STM32的内部SRAM中,然后点击全速运行,
//				可在串口查看调试输出。由于SRAM调试状态下,复位会使芯片程序乱飞,所以每次切
//				换状态后,都要重新点击“Debug”按钮下载SRAM程序,再全速运行查看输出。//3.若自己修改程序导致使芯片处于读写保护状态而无法下载,
//  且 FALSH程序自身又不包含自解除状态的程序,可以使用本工程的“RAM版本”解除,解除即可重新下载。/** 函数名:main* 描述  :主函数* 输入  :无* 输出  :无*/
int main(void)
{ 	/*初始化USART,配置模式为 115200 8-N-1*/USART_Config();LED_GPIO_Config();Key_GPIO_Config();LED_BLUE;//芯片自动复位后,串口可能有小部分异常输出,如输出一个“?”号printf("\r\n欢迎使用野火  STM32  开发板。\r\n");	printf("这是读写保护测试实验\r\n");/* 获取写保护寄存器的值进行判断,寄存器位为0表示有保护,为1表示无保护 *//*  若不等于0xFFFFFFFF,则说明有部分页被写保护了 */if(FLASH_GetWriteProtectionOptionByte() !=0xFFFFFFFF ){printf("\r\n目前芯片处于写保护状态,按Key1键解除保护\r\n");printf("写保护寄存器的值:WRPR=0x%x\r\n",FLASH_GetWriteProtectionOptionByte());}else //无写保护{printf("\r\n目前芯片无 写 保护,按 Key1 键可设置成 写 保护\r\n");printf("写保护寄存器的值:WRPR=0x%x\r\n",FLASH_GetWriteProtectionOptionByte());}/*  若等于SET,说明处于读保护状态 */if(FLASH_GetReadOutProtectionStatus () == SET ){printf("\r\n目前芯片处于读保护状态,按Key2键解除保护\r\n");}else{printf("\r\n目前芯片无 读 保护,按 Key2 键可设置成 读 保护\r\n");}while(1)                            {	   if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON  ){LED1_TOGGLE;WriteProtect_Toggle();} if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON  ){LED2_TOGGLE;ReadProtect_Toggle();			}		}	}void Delay(__IO uint32_t nCount)
{for(; nCount != 0; nCount--);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树的直径计算:算法详解与实现

树的直径计算:算法详解与实现 1. 引言2. 算法概述3. 伪代码实现4. C语言实现5. 算法分析6. 结论在图论中,树的直径是一个关键概念,它表示树中任意两点间最长路径的长度。对于给定的树T=(V,E),其中V是顶点集,E是边集,树的直径定义为所有顶点对(u,v)之间最短路径的最大值。…

无人机场景 - 目标检测数据集 - 车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;无人机场景车辆检测数据集&#xff0c;真实场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如无人机场景城市道路行驶车辆图片、无人机场景城市道边停车车辆图片、无人机场景停车场车辆图片、无人机场景小区车辆图片、无人机场景车辆遮挡、车…

爬虫——Requests库的使用

在爬虫开发中&#xff0c;HTTP请求是与服务器进行交互的关键操作。通过发送HTTP请求&#xff0c;爬虫可以获取目标网页或接口的数据&#xff0c;而有效地处理请求和响应是爬虫能够高效且稳定运行的基础。Requests库作为Python中最常用的HTTP请求库&#xff0c;因其简洁、易用和…

基于python Django的boss直聘数据采集与分析预测系统,爬虫可以在线采集,实时动态显示爬取数据,预测基于技能匹配的预测模型

本系统是基于Python Django框架构建的“Boss直聘”数据采集与分析预测系统&#xff0c;旨在通过技能匹配的方式对招聘信息进行分析与预测&#xff0c;帮助求职者根据自身技能找到最合适的职位&#xff0c;同时为招聘方提供更精准的候选人推荐。系统的核心预测模型基于职位需求技…

pytest | 框架的简单使用

这里写目录标题 单个文件测试方法执行测试套件的子集测试名称的子字符串根据应用的标记进行选择 其他常见的测试命令 pytest框架的使用示例 pytest将运行当前目录及其子目录中test_*.py或 *_test.py 形式的所有 文件 文件内的函数名称可以test* 或者test_* 开头 单个文件测试…

杰控通过 OPCproxy 获取数据发送到服务器

把数据从 杰控 取出来发到服务器 前提你在杰控中已经有变量了&#xff08;wincc 也适用&#xff09; 打开你的opcproxy 软件包 opcvarFile 添加变量 写文件就写到 了 opcproxy.ini中 这个文件里就是会读取到的数据 然后 opcproxy.exe发送到桌面快捷方式再考回来 &#…

Ubuntu 的 ROS 操作系统 turtlebot3 导航仿真

引言 导航仿真是机器人自动化系统中不可或缺的一部分&#xff0c;能够帮助开发者在虚拟环境中测试机器人在复杂场景下的运动与路径规划。 在 Gazebo 仿真环境中&#xff0c;TurtleBot3 配合 ROS 操作系统提供了强大的导航功能。在进行导航仿真时&#xff0c;首先需要准备地图&…

Django5 2024全栈开发指南(一):框架简介、环境搭建与项目结构

目录 一、Python Web框架要点二、Django流程2.1 Django介绍2.1.1 简介2.1.2 特点2.1.3 MVT模式2.1.4 Django新特性2.1.5 Django学习资料 2.2 搭建Django框架开发环境2.2.1 安装Python语言环境2.2.2 安装Django框架 2.3 创建Django项目2.4 Pycharm创建项目2.5 初试Django52.5.1 …

Vue3 -- 项目配置之eslint【企业级项目配置保姆级教程1】

下面是项目级完整配置1➡eslint&#xff1a;【吐血分享&#xff0c;博主踩过的坑你跳过去&#xff01;&#xff01;跳不过去&#xff1f;太过分了给博主打钱】 浏览器自动打开项目&#xff1a; 你想释放双手吗&#xff1f;你想每天早上打开电脑运行完项目自动在浏览器打开吗&a…

【流量分析】常见webshell流量分析

免责声明&#xff1a;本文仅作分享&#xff01; 对于常见的webshell工具&#xff0c;就要知攻善防&#xff1b;后门脚本的执行导致webshell的连接&#xff0c;对于默认的脚本要了解&#xff0c;才能更清晰&#xff0c;更方便应对。 &#xff08;这里仅针对部分后门代码进行流量…

【MQTT.fx 客户端接入 阿里云平台信息配置】

1、打开界面&#xff0c;点击如下图⚙图标 2、点击左下角➕&#xff0c;添加新的配置&#xff0c;Profile Name 同阿里云平台设备名。 3、打开已经配置好的阿里云平台&#xff0c;进入设备信息界面&#xff0c;点击“MQTT连接参数”&#xff0c; 4、其他参数&#xff0c;对…

抽象java入门1.5.3.1——类的进阶

前言&#xff1a;在研究神技代码Hello word的时候&#xff0c;发现了一个重大公式bug&#xff0c;在代码溯源中&#xff0c;我发现了一个奇怪的东西&#xff0c;就是OUT不是类中类&#xff08;不是常规类的写法&#xff09; 内容总结&#xff1a; 代码运行的顺序复习 正片开始…

vue2+3 —— Day5/6

自定义指令 自定义指令 需求&#xff1a;当页面加载时&#xff0c;让元素获取焦点&#xff08;一进页面&#xff0c;输入框就获取焦点&#xff09; 常规操作&#xff1a;操作dom “dom元素.focus()” 获取dom元素还要用ref 和 $refs <input ref"inp" type&quo…

JAVA-链表

1.链表的概念及结构 链表是一种物理存储结构上非连续存储结构(逻辑上连续)&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。 注意&#xff1a; 根据上图可看出&#xff0c;链表是在逻辑结构连续的&#xff0c;但是在物理结构上不一定现实中的结点一般都是通…

RTSP播放器EasyPlayer.js播放器UniApp或者内嵌其他App里面webview需要截图下载

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

DB Type

P位 p 1时段描述符有效&#xff0c;p 0时段描述符无效 Base Base被分成了三个部分&#xff0c;按照实际拼接即可 G位 如果G 0 说明描述符中Limit的单位是字节&#xff0c;如果是G 1 &#xff0c;那么limit的描述的单位是页也就是4kb S位 S 1 表示代码段或者数据段描…

【Fargo】23:采集时间转rtp时间

RTP时间戳 编码会沿用当前时间,以毫秒计算,而rtp传输系统采用的是时间基准并不是当前时间RTP 时间戳为了多媒体不同流之间实现同步而设计的。Mediasoup的clockrate参数就是指定这个的 采集实现戳是当前时间uint32类型的毫秒,如果使用rtp发送h264编码的rtp包,时间戳要怎么打…

Android Osmdroid + 天地图 (一)

Osmdroid 天地图 前言正文一、配置build.gradle二、配置AndroidManifest.xml三、获取天地图的API Key① 获取开发版SHA1② 获取发布版SHA1 四、请求权限五、显示地图六、源码 前言 Osmdroid是一款完全开源的地图基本操作SDK&#xff0c;我们可以通过这个SDK去加一些地图API&am…

HTML5+CSS前端开发【保姆级教学】+新闻文章初体验

Hello&#xff0c;各位编程猿们&#xff01;上一篇文章介绍了前端以及软件的安装&#xff0c;这一篇我们要继续讲解页面更多知识点&#xff0c;教大家做一篇新闻题材的文章 新闻文章 当我们点开浏览器经常看到各种各样的文章&#xff0c;今天我们就来看看大家最喜欢关注的体育…

无人机动力系统测试-实测数据与CFD模拟仿真数据关联对比分析

我们经常被问到这样的问题&#xff1a;“我们计划运行 CFD 仿真&#xff0c;我们还需要对电机和螺旋桨进行实验测试吗&#xff1f;我们可能有偏见&#xff0c;但我们的答案始终是肯定的&#xff0c;而且有充分的理由。我们自己执行了大量的 CFD 仿真&#xff0c;但我们承认&…