STM32保护内部FLASH

news/2024/11/21 13:10:43/

 在实际发布的产品中,在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.ppmy.cn/news/1548763.html

相关文章

初级数据结构——串

目录 前言一、串的定义二、串的存储结构三、串的基本操作四、串的模式匹配五、串的应用六、c代码模版七、经典例题1.汉字统计代码题解 2.查找最大元素代码题解 3.首字母变大写代码题解 八、总结结语 前言 这期我们一起深入学习初级数据结构——串&#xff0c;数据结构中的串&a…

【jvm】如何破坏双亲委派机制

目录 1.说明2.重写ClassLoader的loadClass方法2.1 原理2.2 实现步骤2.3 注意事项 3.使用线程上下文类加载器3.1 原理3.2 实现步骤3.3 应用场景 4.利用SPI机制4.1 原理4.2 实现步骤4.3 应用场景 5.Tomcat等容器的自定义类加载器5.1 原理5.2 实现方式5.3 应用场景 1.说明 1.双亲委…

修改一下达梦disql 提示符

经常用disql的有时某些信息希望提示一下&#xff0c;默认的只显示SQL> 为了方便使用&#xff0c;可以在 glogin.sql 中增加些内容。 vi $DM_HOME/bin/disql_conf/glogin.sql增加以下几行 set time on set lineshow offcol global_name new_value global_name SELECT ins…

云原生学习

1、云原生学习 文章目录 1、云原生学习1. 介绍2. Docker容器化 1. 介绍 什么是云原生&#xff1f;原生指使用JAVA等语言编写的项目&#xff0c;云是指将项目部署到云服务器上云平台&#xff1a;公有云、私有云 本地平台是指直接部署在自己计算机&#xff0c;而开发的应用一定要…

[AI] 【提高认知】自动翻译技术的演变:从规则系统到深度学习的崛起

机器自动翻译 (MT) 是人工智能历史上最早的应用之一,尤其是在英语和俄语之间的翻译应用。自诞生以来,自动翻译技术从符号系统逐步演化到依赖大数据和深度学习的先进模型。本文将深入探讨机器翻译的早期方法、统计方法和现代神经网络方法的演变过程,帮助大家了解自动翻译技术…

人工智能与SEO优化中的关键词策略解析

内容概要 在当今数字化快速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;与搜索引擎优化&#xff08;SEO&#xff09;的结合正变得愈发重要。关键词策略是SEO优化的一项基础工作&#xff0c;它直接影响到网站的可见性和流量。通过运用智能算法&#xff0c;企业能…

VMware Tools工具安装脚本(CentOS Ubuntu)

1、VMware Tools&#xff08;CentOS版&#xff09; #!/bin/bashlog_info() { echo "[INFO] $1" echo "[INFO] $1" >> "$LOGFILE" }log_error() { echo "[ERROR] $1" echo "[ERROR] $1" >> "$LOGFILE"…

第一讲,Opencv计算机视觉基础之计算机视觉概述

深度剖析计算机视觉&#xff1a;定义、任务及未来发展趋势 引言 计算机视觉&#xff08;Computer Vision&#xff09;是人工智能的重要分支之一&#xff0c;旨在让机器通过视觉感知和理解环境。随着深度学习的快速发展&#xff0c;计算机视觉在自动驾驶、安防监控、医疗影像等…