单片机flash写保护_stm32 单片机flash怎么改变写保护? - CSDN
  • Flash擦写的内容,个人做HC9S12系列单片机时觉得应该是各模块内容中最难而且是最麻烦的一步了。只有能够对Flash进行擦写以后,所做的Bootloader才有真正手段将串口或者其他通讯手段接收到的数据或者程序写入Flash中...

           Flash擦写的内容,个人做HC9S12系列单片机时觉得应该是各模块内容中最难而且是最麻烦的一步了。只有能够对Flash进行擦写以后,所做的Bootloader才有真正手段将串口或者其他通讯手段接收到的数据或者程序写入Flash中进行程序或者数据的更新。当初做Flash的擦写也遇到了很多问题,网上搜罗下来也没有合适的帖子能够详细的将整个过程或者学习的思路以及最后实现的程序详细的加以解析,而本博文就是基于这个出发点来写的。技术本身应当是共享的才能真正让社会进步,不是么?

      关于单片机Flash的基本内容,我在此前Bootloader相关的博文中也已经提及。Flash本身是非易失性存储,可以通过编程的方式擦写其中的内容,掉电后其内容不会丢失,一般是单片机的程序存储位置。单片机运行时先将Flash中下一条运行的程序读出,然后执行其内容,再读出下一条指令,再执行循环往复。但是我们希望,能够使我们开发的单片机应用程序能够有自动更新程序的功能。比方说我现在开发的一款共享单车智能车锁,在共享单车在大马路上只能通过网络交互的时候,我希望能够更新该车锁中单片机的程序以提供新的功能。这种时候,我们就需要擦写Flash来完成这一目的。

      Flash擦写操作最应该看的就是单片机的或者Flash的手册,认真的看,通篇阅读后再尝试用C语言去加以实现其功能。我们的讲解也是在这个过程中逐渐展开的,当然,还是针对HC9S12G128系列的单片机内置Flash而言。本次篇博文仅考虑了比较简单的Flash擦除与Flash写入操作,当然,在理解了Flash操作的基础上,Flash的其他操作如写保护,解保护其实是一样的道理。好了,下面我们就开始吧。

         G128系列单片机的Flash存储大小有128KB, 其全局地址范围为: 0x2_0000 - 0x3_FFFF。这篇文章中已经假设各位读者已经对分页地址,非分页地址,逻辑地址,全局地址已经有所理解,如果尚不明确其意义的,我在freescale飞思卡尔 HCS12 系列单片机bootloader详解(二)这篇博文中做了解释,理解了这些概念间的相互关系再来看本博文会比较有帮助。了解Flash擦写前,根据我们手册中的内容,Flash的控制体系是这样的:

      在S12系列单片机中,编程人员对Flash的操作并不是实际意义上对每个Flash存储区直接进行操作的,而是通过这个Flash Interface进行的。通过对这个Flash Interface的寄存器进行配置,再由它对Flash进行直接的操作。这里,我们称Flash Interface为Flash操作控制器或者Flash控制器。那么如何控制Flash控制器呢?手册中已经给出了Flash操作控制相关的所有寄存器,如下图所示,这些寄存器就是我们单片机的寄存器,它们的操作与普通的寄存器没有什么两样,在单片机寄存器定义头文件中均可以找到。还有一点就是通过总线时钟按照对应单片机Flash控制器的要求设置时钟分频器,给你使用的Flash控制器设置合适的时钟频率使其能够正常工作。

    HCS12G128单片机部分Flash控制器相关的操作寄存器(不同型号单片机会有所不同)

      首要的任务当然是设置Flash控制器的时钟了,它以总线时钟为时钟源,通过设置FCLKDIV(Flash时钟分频寄存器)来对其进行设置。G128的FCLKDIV寄存器定义在手册中的内容如下:

      在这页手册中可以看到,有三个寄存器需要处理FDIVLD,FDIVLCK,FDIV。先说FDIV寄存器,这个寄存器有六位,通过分频将总线时钟频率分频至1MHz以下从而使Flash控制器可以正常工作。FDIV的值与总线时钟频率有关,根据总线频率的大小确定FDIV的值,其取值在手册中也给出了下表:

      对于这个表,用法也非常简单,假如当前我的总线频率为15MHz,查表中15MHz在14.6与15.6之间,那么FDIV的值就是0x0E了。当确定好Flash控制器的频率后需要将其写保护以防误操作修改了分频寄存器,那么对FDIVLCK写1就好了,当FDIVLCK写入1后,除非重启,否则FDIV的值不能被修改,重启后FDIVLCK的值将重新归零。

      由此总结我们Flash控制器的时钟设置步骤如下:

      1. 根据总线频率设置分频FDIV

      2. 对分频进行保护,将FDIVLCK置为1

      这里需要注意,当需要写入FCLKDIV这个寄存器(也就是完成上面两个操作时),一定要确保此时Flash控制器不在执行指令,那么Flash控制器如何执行指令的呢?怎么查看它是在执行指令的呢?

      请看下节,Flash控制器的指令寄存器

           注: 本系列文章均为原创,如有转载引用请标明来源 

     

    转载于:https://www.cnblogs.com/15821216114sw/p/9509680.html

    展开全文
  • 一般32位单片机的内部FALSH是不支持字节操作的,有的可以按字节读取,但是不能按字节写入。 而且,一般单片机内部FALSH擦除的最小单位都是页,如果向某页中的某个位置写入数据,恰好这个位置的前面存了其他数据,...

    一般32位单片机的内部FALSH是不支持字节操作的,有的可以按字节读取,但是不能按字节写入。

    而且,一般单片机内部FALSH擦除的最小单位都是页,如果向某页中的某个位置写入数据,恰好这个位置的前面存了其他数据,那么就必须把这页擦除,存的其他数据也会丢失。

    实际上就是说内部的FALSH不好做改写的操作,如果有很多数据需要存放,最好是分页存储。这也是FALSH与E2PROM最大的区别,后者支持按字节操作且无需擦除,即使某一个地址写坏了,也不影响其他地址。

    下面介绍一种方法让内部FLASH"支持"字节操作,且同一页的其他数据不受影响。

    方法原理很简单,下面简单介绍下原理:

    1.根据要写入地址,计算出该地址位于哪一页;

    2.读出整个页,存入缓存BUF;

    3.将要写入的数据按位置更新到BUF中;

    4.擦除该页;

    5.写入整个BUF。

    可以看出这种方法弊端很明显:

    1.耗时长  每次写都要读整个BUF,然后还要先把数据存到BUF里,然后再写入整个BUF;

    2.FALSH擦写次数增加,降低使用寿命;

    下面给出测试代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>//C语言标准库
    #include "flash.h"
    
    #define USER_FLASH_START_ADDR   0x01070000   //FLASH最后两个扇区  供用户使用
    
    
    u32tou8 u32data;//定义一个联合体
    
    //==================================================================================
    // 获取某个地址所在的页首地址
    // addr:FLASH地址
    // 返回:该地址所在的页 共128页(0~127)
    //==================================================================================
    unsigned int FLASH_GetFlashPage(unsigned int addr)
    {
    	  if (IS_FLASH_ADDRESS(addr))
    		{
    		   return  (addr&(~0xFFF));//清0低12位就是该页的起始地址
      	} 
    }
    //==================================================================================
    // 从FLASH中读取 一个字(32位)
    // addr:读取地址
    // 返回: 读到的字数据
    //备注: 地址为4字节对齐
    //==================================================================================
    unsigned int FLSAH_ReadWord(unsigned int addr)
    {
        return (*(unsigned int *)addr);
    }
    
    
    //==================================================================================
    //从FLASH指定地址 读取数据
    //备注: 读取数据类型为32位  读取地址为4字节对齐
    //==================================================================================
    void  FLASH_Read(unsigned int	ReadAddr,unsigned char *pBuffer,unsigned int NumToRead)
    {
        unsigned int i;
    	  u32tobyte cache;
        for(i=0; i<NumToRead; i+=4)
        {
                cache.u32data=FLSAH_ReadWord(ReadAddr+i);
    				    pBuffer[i]=cache.buf[0];
       					pBuffer[i+1]=cache.buf[1];
    					  pBuffer[i+2]=cache.buf[2];
    					  pBuffer[i+3]=cache.buf[3];
        }
    }
    
    //==================================================================================
    // 向FLASH指定地址 写入大量数据
    // WriteAddr:写入首地址
    // pBuffer:数据首地址
    // NumToWrite:需要写入数据的大小
    // 返回: 4=成功  1,2,3,5=失败
    // 备注:
    //==================================================================================
    FLASH_Status  FLASH_Write(unsigned int	WriteAddr,unsigned char *pBuffer,unsigned int NumToWrite)
    {
    
        FLASH_Status status = FLASH_COMPLETE;
    	  u32tobyte cache;//联合体定义
        unsigned int startaddr,endaddr,pageaddr=0;
    	  unsigned char buffer[4096];//4K缓冲区 对应FALSH 1页
    	  unsigned int i;
    	  unsigned int index,remain;
        startaddr = WriteAddr;
        endaddr = startaddr+NumToWrite;//结束地址
    	
        FLASH_Unlock();
        FCU->RO = 0;//去掉所有扇区写保护
        //==================================================================================
        // 判断写入地址是否非法  起始地址或者结束地址不在FALSH范围内则退出
        //==================================================================================
        if(!(IS_FLASH_ADDRESS(startaddr)&& IS_FLASH_ADDRESS(endaddr))) return FLASH_ERROR_PG;
       
    	   while(startaddr < endaddr)
    		 {
    			 
    		//==================================================================================
        //1.计算起始地址在FALSH哪一页,并获取该页的首地址
    		//2.计算起始地址在该页的偏移量
        //3.计算该页还剩余多少字节没写入数据			 
        //==================================================================================
    			  pageaddr = FLASH_GetFlashPage(startaddr);//获取起始地址所在页的页首地址
    			  index = startaddr-pageaddr;//4K缓冲区内偏移地址
    			  remain=4096-index;//缓存区剩余大小
        //==================================================================================
        // 将该页数据读入4K缓冲数组,后面读写都是对该缓冲数组操作
        //==================================================================================			 
    		    for(i=0;i<4096;i+=4)//读取一页到缓冲buff
    			  {
    				    cache.u32data=FLSAH_ReadWord(pageaddr+i);
    				    buffer[i]=cache.buf[0];
       					buffer[i+1]=cache.buf[1];
    					  buffer[i+2]=cache.buf[2];
    					  buffer[i+3]=cache.buf[3];
    				} 
        //==================================================================================
        // 擦除FALSH对应的页,FLASH只能按页擦除,
    		// 这一页数据已经被读到缓冲数组中了 之前的数据也保留下来了 		
        //==================================================================================				
    				status = FLASH_ErasePage(startaddr);
            if(status != FLASH_COMPLETE) return status;//擦除1页 4K字节					
        //==================================================================================
        //1.判断要写入的数据是否大于该页剩余容量(即计算写入的数据长度是否跨多页) 
    		//2.将需要写入的数据转存到缓冲数据		
        //==================================================================================				
    				if(NumToWrite > remain)//需要写入的数据量大于缓冲buf剩余字节数
    				{
    					for(i=index;i<4096;i++)//将需要写入FALSH的数据写入缓冲buff
    					{
    							 buffer[i]=*(pBuffer++);				
    					}
    					NumToWrite-=remain;//需要写入的数据长度-本次已经写入的数据长度	
              startaddr+=remain;//地址向后偏移本次写入的字节数					
    			  }
    				else
    				{
    				  for(i=index;i<NumToWrite+index;i++)//将需要写入FALSH的数据写入缓冲buff
    					{
    							 buffer[i]=*(pBuffer++);				
    					} 
              startaddr+=NumToWrite;//地址向后偏移本次写入的字节数								
    				}	
        //==================================================================================
        // 将缓冲数组(4K)写入FLASH 对应的页
    		// 此处必须从页首写入,因为缓冲数组正好4K,对应FALSH 1页	
        //==================================================================================				
    				for(i=0;i<4096;i+=4)//将缓冲buffer写入 FALSH
    			  {
    				    cache.buf[0]=buffer[i];
       					cache.buf[1]=buffer[i+1];
    					  cache.buf[2]=buffer[i+2];
    					  cache.buf[3]=buffer[i+3];
    					
    					  if((status=FLASH_ProgramWord(pageaddr+i,cache.u32data))!= FLASH_COMPLETE)
    						{
    								FLASH_Lock();
    								return status;//写入失败 FLASH上锁 
    						}	             						
    				}			
    		 }
        FLASH_Lock();
    }
    
    
    
    
    
    
    
    
    
    
    
    

    其中还有个联合体的定义: 

    typedef union
    {
        unsigned int  data;
        unsigned char buf[4];
    }
    u32tou8;

    FLASH_ErasePage、FLASH_ProgramWord、IS_FLASH_ADDRESS 这三个都是单片机FLASH的库函数

    各家单片机不同,但功能基本相同,这里不再提供源码。

    最后提供以下两个FLASH接口即可:

    FLASH_Write(unsigned int    WriteAddr,unsigned char *pBuffer,unsigned int NumToWrite);
    
    FLASH_Read(unsigned int    ReadAddr,unsigned char *pBuffer,unsigned int NumToRead)

    演示:

    1.为方便查看结果,测试从0x1070FFC的位置开始写入数据,FLASH地址分布如下图所示:

    这里展示了FLASH连续两页的地址,首先将这两页全部擦除。

     

    2.接着从1070FFC的位置开始写入56个1,这样就保证了数据跨越了1页。 

    unsigned char write[]= {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111"};
    FLASH_Write(0x01070FFC,write,sizeof(write));

     

     注意:最后的00是因为字符串的结尾字符是“\0”

    3.紧接着,在0x1070FFE位置写入新的字符串,也要保证写入长度跨越1页

    unsigned char write2[]={"23456789"};
    FLASH_Write(0x01070FFE,write2,sizeof(write2));

     

    可以看出,0x1070FFE~0x1071006的位置被写入了新的字节,但这两页的其他位置数据保持不变。

     

    总结:

    1.实际使用时,如果不是受限于成本或者FLASH大小,不建议这样读写内部FLASH,以为stm32内部FLASH也就

    10W次寿命,这样频繁擦写会大大降低FLASH寿命。

    2.如果保存的数据不多,建议每个数据都单独存1页,这样不用考虑擦除时会把其他数据也一并擦除。

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • Flash函数 for(writelen=0;writelen(fileHd);) { uiLen = OSFileRead(GpcBufFileRead, sizeof(GpcBufFileRead), fileHd); /* 读出文件 */ dstaddr = (uint32_t)(APP_START_ADDR + writelen);//dst ...
  • 通过J-Flash STM32 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器...

    一. 写保护

    1. 目的

      将Flash设置为写保护的目的,是为了防止其他人通过J-Link,ULINK2等仿真器,将Flash中的程序读取出来(设想一下,你辛辛苦苦研发的产品,别人通过仿真器将程序读取出来,再copy一下产品的硬件,就可以生产)。
      可以通过将Flash设置为读保护来保护自己的程序。

    2. 开发环境

    适用于STM32F1和F4系列(其他系列没有用过);
    F1系列的库:STM32F10x_StdPeriph_Lib_V3.5.0
    F4系列的库:STM32F4xx_DSP_StdPeriph_Lib_V1.8.0
    开发环境使用的MDK,版本5.25

    3. 程序

      通过flash_if.c源程序中的FLASH_If_EnableReadProtection()函数来加密Flash。函数代码如下:

      /**
        * @brief  Enable the read protection of user flash area.
        * @param  None
        * @retval 1: Read Protection successfully enable
        *         2: Error: Flash read unprotection failed
        */
    
        uint32_t FLASH_If_EnableReadProtection(void)
        {
          /* Returns the FLASH Read Protection level. */
          if( FLASH_OB_GetRDP() == RESET )
          {
            /* Unlock the Option Bytes */
            FLASH_OB_Unlock();
            
            /* Sets the read protection level. */
            FLASH_OB_RDPConfig(OB_RDP_Level_1);
            
            /* Start the Option Bytes programming process. */  
            if (FLASH_OB_Launch() != FLASH_COMPLETE)
            {
              /* Disable the Flash option control register access (recommended to protect 
                 the option Bytes against possible unwanted operations) */
              FLASH_OB_Lock();
              
              /* Error: Flash read unprotection failed */
              return (2);
            }
          
            /* Disable the Flash option control register access (recommended to protect 
               the option Bytes against possible unwanted operations) */
            FLASH_OB_Lock();
        
            /* Read Protection successfully enable */
            return (1);
          }
          
          /* Read Protection successfully enable */
          return (1);
        }
    

      一般在BootLoader的主函数里,加入FLASH_If_EnableReadProtection()函数调用,加密Flash。
      加密后,无法再通过仿真器来Debug或烧写程序,只能通过串口等IAP方式来烧写用户程序。
      工程需要添加flash_if.h和flash_if.c文件。
    在这里插入图片描述

    二. 解除写保护

    有两种方法可以解除Flash的写保护。

    1. 建立MDK工程,程序设置为SRAM启动,在程序中解除Flash的锁定。

    以作者最近使用的STM32F412为例,新建MDK工程,设置Target选项卡:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190327162525708.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3podDIzNzAyMDE=,size_16,color_FFFFFF,t_70
    配置C/C++选项卡,根据芯片型号,包含对应宏定义;预编译宏VECT_TAB_SRAM为必添加项。
    在这里插入图片描述
    添加初始化文件路径:G:\ProgrammeFiles\Keil5\ARM\Pack\Keil\STM32F4xx_DFP\2.13.0\MDK\Boards\Keil\MCBSTM32F400\Blinky\Debug_RAM.ini (MDK的安装路径不同这里有所不同)
    在这里插入图片描述
    在main函数中调用Flash_DisableReadProtection()函数,函数代码如下:

    /****************************************************************
     * Function:    Flash_DisableReadProtection
     * Description: Disable the read protection of user flash area.
     * Input:
     * Output:
     * Return:      1: Read Protection successfully disable
     *              2: Error: Flash read unprotection failed
    *****************************************************************/
    uint32_t Flash_DisableReadProtection(void)
    {
      /* Returns the FLASH Read Protection level. */
      if( FLASH_OB_GetRDP() != RESET )
      {
        /* Unlock the Option Bytes */
        FLASH_OB_Unlock();
        
    /* Sets the read protection level. */
    FLASH_OB_RDPConfig(OB_RDP_Level_0);
    
    /* Start the Option Bytes programming process. */  
    if (FLASH_OB_Launch() != FLASH_COMPLETE)
    {
      /* Disable the Flash option control register access (recommended to protect 
         the option Bytes against possible unwanted operations) */
      FLASH_OB_Lock();
      
      /* Error: Flash read unprotection failed */
      return (2);
    }
    
    /* Disable the Flash option control register access (recommended to protect 
       the option Bytes against possible unwanted operations) */
    FLASH_OB_Lock();
    
    /* Read Protection successfully disable */
    return (1);
      }
      
      /* Read Protection successfully disable */
      return (1);
    }
    

    该函数调用完成后,Flash芯片也已经解锁成功。
    Flash芯片内的程序会被清除。

    2. 通过J-Flash解锁

    1中的方法需要自己新建MDK工程,比较繁琐。
    可以通过JFlash软件,配合JLink仿真器来解锁。
    JFlash软件的下载网址为:
    https://www.segger.com/downloads/jlink/JLink_Windows.exe
    下载安装后,在安装目录下会看到JFlash软件
    在这里插入图片描述
    打开JFlash,选择Create a new project
    在这里插入图片描述
    注意:在弹出的Create New Project对话框中,Speed(kHz)默认是4000,即4MH在,一定要选择200k以下。
    因为SWD总线布线太长或者不规范,若使用默认的下载速度(4MHZ),下载或者擦除芯片时会导致出现下述错误。
    在这里插入图片描述
      在Target Device中选择对应要解锁的芯片。
      以本人所用的STM32F412CE为例,Flash size为512KB,本应选择STM32F411CE,由于STM32F411CE的Flash size为512KB+16MB,因此,选择STM32F411VE。
      只要Flash size大小与要解锁的芯片的Flash大小一致,Device可以不一致。
    在这里插入图片描述通过SWD将芯片与JLink连接后,选择Target-Connect即可连接成功。
    在这里插入图片描述
    再选择Target-Manual Programming-Erase Chip,即可解除芯片的Flash锁定状态。
    不过Flash内的所有数据都会被清除。
    在这里插入图片描述

    展开全文
  • 在实际发布的产品中,在STM32芯片的内部FLASH存储了控制程序,如果不作任何保护措施的话,可以使用下载器直接把内部FLASH的内容读取回来,得到bin或hex文件格式的代码拷贝,别有用心的厂商即可利用该代码文件山寨...

    转载地址:https://www.cnblogs.com/firege/

    51.1 选项字节与读写保护

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

    51.1.1 选项字节的内容

    选项字节是一段特殊的FLASH空间,STM32芯片会根据它的内容进行读写保护、复位电压等配置,选项字节的构成见表 511。

    表 511 选项字节的构成

    地址

    [63:16]

    [15:0]

    0x1FFF C000

    保留

    ROP 和用户选项字节 (RDP & USER)

    0x1FFF C008

    保留

    扇区 0 到 11 的写保护 nWRP 位

    0x1FFE C000

    保留

    保留

    0x1FFE C008

    保留

    扇区 12 到 23 的写保护 nWRP 位

    选项字节具体的数据位配置说明见表 512。

    表 512 选项字节具体的数据位配置说明

    选项字节(字,地址 0x1FFF C000)

    RDP:读保护选项字节。
    读保护用于保护 Flash 中存储的软件代码。

    位 15:8

    0xAA:级别 0,无保护
    其它值:级别 1,存储器读保护(调试功能受限)
    0xCC:级别 2,芯片保护(禁止调试和从 RAM 启动)

    USER:用户选项字节
    此字节用于配置以下功能:
    选择看门狗事件:硬件或软件
    进入停止模式时产生复位事件
    进入待机模式时产生复位事件

    位 7

    nRST_STDBY
    0:进入待机模式时产生复位
    1:不产生复位

    位 6

    nRST_STOP
    0:进入停止模式时产生复位
    1:不产生复位

    位 5

    WDG_SW
    0:硬件看门狗
    1:软件看门狗

    位 4

    0x1:未使用

    位 3:2

    BOR_LEV: BOR 复位级别
    这些位包含释放复位信号所需达到的供电电压阈值。
    通过对这些位执行写操作,可将新的 BOR 级别值编程到 Flash。
    00: BOR 级别 3 (VBOR3),复位阈值电压为 2.70 V 到 3.60 V
    01: BOR 级别 2 (VBOR2),复位阈值电压为 2.40 V 到 2.70 V
    10: BOR 级别 1 (VBOR1),复位阈值电压为 2.10 V 到 2.40 V
    11: BOR 关闭 (VBOR0),复位阈值电压为 1.8 V 到 2.10 V

    位 1:0

    0x1:未使用

    选项字节(字,地址 0x1FFF C008)

    位15

    SPMOD:选择nWPRi位的模式
    0:nWPRi位用于写保护(默认)
    1:nWPRi位用于代码读出保护(Proprietary code readout protection ,PCROP)

    位14

    DB1M:设置内部FLASH为1MB的产品的双Bank模式
    0:单个Bank的模式
    1:使用两个Bank的模式

    位 13:12

    0xF:未使用

    nWRP: Flash 写保护选项字节。
    扇区 0 到 11 可采用写保护。

    位 11:0

    nWRPi (i值为0-11,对应0-11扇区的保护设置):
    0:开启所选扇区的写保护
    1:关闭所选扇区的写保护

    选项字节(字,地址 0x1FFE C000)

    位 15:0

    0xFF:未使用

    选项字节(字,地址 0x1FFE C008)

    位 15:12

    0xF:未使用

    nWRP: Flash 写保护选项字节。
    扇区 12 到 23 可采用写保护。

    位 11:0

    nWRPi:
    0:开启扇区 i 的写保护。
    1:关闭扇区 i 的写保护。

    我们主要讲解选项字节配置中的RDP位和PCROP位,它们分别用于配置读保护级别及代码读出保护。

    51.1.2 RDP读保护级别

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

        0xAA:级别0,无保护

    这是STM32的默认保护级别,它没有任何读保护,读取内部FLASH及"备份SRAM"的内容都没有任何限制。(注意这里说的"备份SRAM"是指STM32备份域的SRAM空间,不是指主SRAM,下同)

        其它值:级别1,使能读保护

    把RDP配置成除0xAA或0xCC外的任意数值,都会使能级别1的读保护。在这种保护下,若使用调试功能(使用下载器、仿真器)或者从内部SRAM自举时都不能对内部FLASH及备份SRAM作任何访问(读写、擦除都被禁止);而如果STM32是从内部FLASH自举时,它允许对内部FLASH及备份SRAM的任意访问。

    也就是说,在级别1模式下,任何尝试从外部访问内部FLASH内容的操作都被禁止,例如无法通过下载器读取它的内容,或编写一个从内部SRAM启动的程序,若该程序读取内部FLASH,会被禁止。而如果是芯片自己访问内部FLASH,是完全没有问题的,例如前面的"读写内部FLASH"实验中的代码自己擦写内部FLASH空间的内容,即使处于级别1的读保护,也能正常擦写。

    当芯片处于级别1的时候,可以把选项字节的RDP位重新设置为0xAA,恢复级别0。在恢复到级别0前,芯片会自动擦除内部FLASH及备份SRAM的内容,即降级后原内部FLASH的代码会丢失。在级别1时使用SRAM自举的程序也可以访问选项字节进行修改,所以如果原内部FLASH的代码没有解除读保护的操作时,可以给它加载一个SRAM自举的程序进行保护降级,后面我们将会进行这样的实验。

        0xCC:级别2,禁止调试

    把RDP配置成0xCC值时,会进入最高级别的读保护,且设置后无法再降级,它会永久禁止用于调试的JTAG接口(相当于熔断)。在该级别中,除了具有级别1的所有保护功能外,进一步禁止了从SRAM或系统存储器的自举(即平时使用的串口ISP下载功能也失效),JTAG调试相关的功能被禁止,选项字节也不能被修改。它仅支持从内部FLASH自举时对内部FLASH及SRAM的访问(读写、擦除)。

    由于设置了级别2后无法降级,也无法通过JTAG、串口ISP等方式更新程序,所以使用这个级别的保护时一般会在程序中预留"后门"以更新应用程序,若程序中没有预留后门,芯片就无法再更新应用程序了。所谓的"后门"是一种IAP程序(In Application Program),它通过某个通讯接口获取将要更新的程序内容,然后利用内部FLASH擦写操作把这些内容烧录到自己的内部FLASH中,实现应用程序的更新。

    不同级别下的访问限制见图 511。

    图 511 不同级别下的访问限制

    不同保护级别之间的状态转换见图 512。

    图 512 不同级别间的状态转换

    51.1.3 PCROP代码读出保护

    在STM32F42xx及STM32F43xx系列的芯片中,除了可使用RDP对整片FLASH进行读保护外,还有一个专用的代码读出保护功能(Proprietary code readout protection,下面简称PCROP),它可以为内部FLASH的某几个指定扇区提供保护功能,所以它可以用于保护一些IP代码,方便提供给另一方进行二次开发,见图 513。

    图 513 PCROP保护功能

    当SPMOD位设置为0时(默认值),nWRPi位用于指定要进行写保护的扇区,这可以防止错误的指针操作导致FLASH 内容的改变,若扇区被写保护,通过调试器也无法擦除该扇区的内容;当SPMOD位设置为1时,nWRPi位用于指定要进行PCROP保护的扇区。其中PCROP功能可以防止指定扇区的FLASH内容被读出,而写保护仅可以防止误写操作,不能被防止读出。

    当要关闭PCROP功能时,必须要使芯片从读保护级别1降为级别0,同时对SPMOD位置0,才能正常关闭;若芯片原来的读保护为级别0,且使能了PCROP保护,要关闭PCROP时也要先把读保护级别设置为级别1,再在降级的同时设置SPMOD为0。

    51.2 修改选项字节的过程

    修改选项字节的内容可修改各种配置,但是,当应用程序运行时,无法直接通过选项字节的地址改写它们的内容,例如,接使用指针操作地址0x1FFFC0000的修改是无效的。要改写其内容时必须设置寄存器FLASH_OPTCR及FLASH_OPTCR1中的对应数据位,寄存器的与选项字节对应位置见图 514及图 515,详细说明请查阅《STM32参考手册》。

    图 514 FLASH_OPTCR寄存器说明(nWRP表示0-11扇区)

    图 515 FLASH_OPTCR1寄存器说明(nWRP表示12-23扇区)

    默认情况下,FLASH_OPTCR寄存器中的第0位OPTLOCK值为1,它表示选项字节被上锁,需要解锁后才能进行修改,当寄存器的值设置完成后,对FLASH_OPTCR寄存器中的第1位OPTSTRT值设置为1,硬件就会擦除选项字节扇区的内容,并把FLASH_OPTCR/1寄存器中包含的值写入到选项字节。

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

    (1)    解锁,在 Flash 选项密钥寄存器 (FLASH_OPTKEYR) 中写入 OPTKEY1 = 0x0819 2A3B;接着在 Flash 选项密钥寄存器 (FLASH_OPTKEYR) 中写入 OPTKEY2 = 0x4C5D 6E7F。

    (2)    检查 FLASH_SR 寄存器中的 BSY 位,以确认当前未执行其它Flash 操作。

    (3)    在 FLASH_OPTCR 和/或 FLASH_OPTCR1 寄存器中写入选项字节值。

    (4)    将 FLASH_OPTCR 寄存器中的选项启动位 (OPTSTRT) 置 1。

    (5)    等待 BSY 位清零,即写入完成。

    51.3 操作选项字节的库函数

    为简化编程,STM32标准库提供了一些库函数,它们封装了修改选项字节时操作寄存器的过程。

    1.    选项字节解锁、上锁函数

    对选项字节解锁、上锁的函数见代码清单 511。

    代码清单 511选项字节解锁、上锁

    1

    2 #define FLASH_OPT_KEY1 ((uint32_t)0x08192A3B)

    3 #define FLASH_OPT_KEY2 ((uint32_t)0x4C5D6E7F)

    4

    5 /**

    6 * @brief Unlocks the FLASH Option Control Registers access.

    7 * @param None

    8 * @retval None

    9 */

    10 void FLASH_OB_Unlock(void)

    11 {

    12 if((FLASH->OPTCR & FLASH_OPTCR_OPTLOCK) != RESET)

    13 {

    14 /* Authorizes the Option Byte register programming */

    15 FLASH->OPTKEYR = FLASH_OPT_KEY1;

    16 FLASH->OPTKEYR = FLASH_OPT_KEY2;

    17 }

    18 }

    19

    20 /**

    21 * @brief Locks the FLASH Option Control Registers access.

    22 * @param None

    23 * @retval None

    24 */

    25 void FLASH_OB_Lock(void)

    26 {

    27 /* Set the OPTLOCK Bit to lock the FLASH Option Byte Registers access */

    28 FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;

    29 }

    解锁的时候,它对FLASH_OPTCR寄存器写入两个解锁参数,上锁的时候,对FLASH_ OPTCR寄存器的FLASH_OPTCR_OPTLOCK位置1。

    2.    设置读保护级别

    解锁后设置选项字节寄存器的RDP位可调用FLASH_OB_RDPConfig完成,见代码清单 512。

    代码清单 512 设置读保护级别

    1 /**

    2 * @brief Sets the read protection level.

    3 * @param OB_RDP: specifies the read protection level.

    4 * This parameter can be one of the following values:

    5 * @arg OB_RDP_Level_0: No protection

    6 * @arg OB_RDP_Level_1: Read protection of the memory

    7 * @arg OB_RDP_Level_2: Full chip protection

    8 *

    9 * /!\ Warning /!\ When enabling OB_RDP level 2 it's no more possible to go back to level 1 or 0

    10 *

    11 * @retval None

    12 */

    13 void FLASH_OB_RDPConfig(uint8_t OB_RDP)

    14 {

    15 FLASH_Status status = FLASH_COMPLETE;

    16

    17 /* Check the parameters */

    18 assert_param(IS_OB_RDP(OB_RDP));

    19

    20 status = FLASH_WaitForLastOperation();

    21

    22 if(status == FLASH_COMPLETE)

    23 {

    24 *(__IO uint8_t*)OPTCR_BYTE1_ADDRESS = OB_RDP;

    25

    26 }

    27 }

    该函数根据输入参数设置RDP寄存器位为相应的级别,其注释警告了若配置成OB_RDP_Level_2会无法恢复。类似地,配置其它选项时也有相应的库函数,如FLASH_OB_PCROP1Config、FLASH_OB_WRP1Config分别用于设置要进行PCROP保护或WRP保护(写保护)的扇区。

    3.    写入选项字节

    调用上一步骤中的函数配置寄存器后,还要调用代码清单 513中的FLASH_OB_Launch函数把寄存器的内容写入到选项字节中。

    代码清单 513 写入选项字节

    1 /**

    2 * @brief Launch the option byte loading.

    3 * @param None

    4* @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PROGRAM,

    5 * FLASH_ERROR_WRP, FLASH_ERROR_OPERATION or FLASH_COMPLETE.

    6 */

    7 FLASH_Status FLASH_OB_Launch(void)

    8 {

    9 FLASH_Status status = FLASH_COMPLETE;

    10

    11 /* Set the OPTSTRT bit in OPTCR register */

    12 *(__IO uint8_t *)OPTCR_BYTE0_ADDRESS |= FLASH_OPTCR_OPTSTRT;

    13

    14 /* Wait for last operation to be completed */

    15 status = FLASH_WaitForLastOperation();

    16

    17 return status;

    18 }

    该函数设置FLASH_OPTCR_OPTSTRT位后调用了FLASH_WaitForLastOperation函数等待写入完成,并返回写入状态,若操作正常,它会返回FLASH_COMPLETE。

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

    在本实验中我们将以实例讲解如何修改选项字节的配置,更改读保护级别、设置PCROP或写保护,最后把选项字节恢复默认值。

    本实验要进行的操作比较特殊,在开发和调试的过程中都是在SRAM上进行的(使用SRAM启动方式)。例如,直接使用FLASH版本的程序进行调试时,如果该程序在运行后对扇区进行了写保护而没有解除的操作或者该解除操作不正常,此时将无法再给芯片的内部FLASH下载新程序,最终还是要使用SRAM自举的方式进行解除操作。所以在本实验中为便于修改选项字节的参数,我们统一使用SRAM版本的程序进行开发和学习,当SRAM版本调试正常后再改为FLASH版本。

    关于在SRAM中调试代码的相关配置,请参考前面的章节。

    注意:

    若您在学习的过程中想亲自修改代码进行测试,请注意备份原工程代码。当芯片的FLASH被保护导致无法下载程序到FLASH时,可以下载本工程到芯片,并使用SRAM启动运行,即可恢复芯片至默认配置。但如果修改了读保护为级别2,采用任何方法都无法恢复!(除了这个配置,其它选项都可以大胆地修改测试。)

    51.4.1 硬件设计

    本实验在SRAM中调试代码,因此把BOOT0和BOOT1引脚都使用跳线帽连接到3.3V,使芯片从SRAM中启动。

    51.4.2 软件设计

    本实验的工程名称为"设置读写保护与解除",学习时请打开该工程配合阅读,它是从"RAM调试—多彩流水灯"工程改写而来的。为了方便展示及移植,我们把操作内部FLASH相关的代码都编写到"internalFlash_reset.c"及"internalFlash_reset.h"文件中,这些文件是我们自己编写的,不属于标准库的内容,可根据您的喜好命名文件。

    1.    主要实验

    (1)    学习配置扇区写保护;

    (2)    学习配置PCROP保护;

    (3)    学习配置读保护级别;

    (4)    学习如何恢复选项字节到默认配置;

    2.    代码分析

    配置扇区写保护

    我们先以代码清单 514中的设置与解除写保护过程来学习如何配置选项字节。

    代码清单 514 配置扇区写保护

    1

    2 #define FLASH_WRP_SECTORS (OB_WRP_Sector_0|OB_WRP_Sector_1)

    3 __IO uint32_t SectorsWRPStatus = 0xFFF;

    4

    5 /**

    6 * @brief WriteProtect_Test,普通的写保护配置

    7 * @param 运行本函数后会给扇区FLASH_WRP_SECTORS进行写保护,再重复一次会进行解写保护

    8 * @retval None

    9 */

    10 void WriteProtect_Test(void)

    11 {

    12 FLASH_Status status = FLASH_COMPLETE;

    13 {

    14 /* 获取扇区的写保护状态 */

    15 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;

    16

    17 if (SectorsWRPStatus == 0x00)

    18 {

    19 /* 扇区已被写保护,执行解保护过程*/

    20

    21 /* 使能访问OPTCR寄存器 */

    22 FLASH_OB_Unlock();

    23

    24 /* 设置对应的nWRP位,解除写保护 */

    25 FLASH_OB_WRPConfig(FLASH_WRP_SECTORS, DISABLE);

    26 status=FLASH_OB_Launch();

    27 /* 开始对选项字节进行编程 */

    28 if (status != FLASH_COMPLETE)

    29 {

    30 FLASH_ERROR("对选项字节编程出错,解除写保护失败,status = %x",status);

    31 /* User can add here some code to deal with this error */

    32 while (1)

    33 {

    34 }

    35 }

    36 /* 禁止访问OPTCR寄存器 */

    37 FLASH_OB_Lock();

    38

    39 /* 获取扇区的写保护状态 */

    40 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;

    41

    42 /* 检查是否配置成功 */

    43 if (SectorsWRPStatus == FLASH_WRP_SECTORS)

    44 {

    45 FLASH_INFO("解除写保护成功!");

    46 }

    47 else

    48 {

    49 FLASH_ERROR("未解除写保护!");

    50 }

    51 }

    52 else

    53 { /* 若扇区未被写保护,开启写保护配置 */

    54

    55 /* 使能访问OPTCR寄存器 */

    56 FLASH_OB_Unlock();

    57

    58 /*使能 FLASH_WRP_SECTORS 扇区写保护 */

    59 FLASH_OB_WRPConfig(FLASH_WRP_SECTORS, ENABLE);

    60

    61 status=FLASH_OB_Launch();

    62 /* 开始对选项字节进行编程 */

    63 if (status != FLASH_COMPLETE)

    64 {

    65 FLASH_ERROR("对选项字节编程出错,设置写保护失败,status = %x",status);

    66 while (1)

    67 {

    68 }

    69 }

    70 /* 禁止访问OPTCR寄存器 */

    71 FLASH_OB_Lock();

    72

    73 /* 获取扇区的写保护状态 */

    74 SectorsWRPStatus = FLASH_OB_GetWRP() & FLASH_WRP_SECTORS;

    75

    76 /* 检查是否配置成功 */

    77 if (SectorsWRPStatus == 0x00)

    78 {

    79 FLASH_INFO("设置写保护成功!");

    80 }

    81 else

    82 {

    83 FLASH_ERROR("设置写保护失败!");

    84 }

    85 }

    86 }

    87 }

    本函数分成了两个部分,它根据目标扇区的状态进行操作,若原来扇区为非保护状态时就进行写保护,若为保护状态就解除保护。其主要操作过程如下:

        调用FLASH_OB_GetWRP函数获取目标扇区的保护状态若扇区被写保护,则开始解除保护过程,否则开始设置写保护过程;

        调用FLASH_OB_Unlock解锁选项字节的编程;

        调用FLASH_OB_WRPConfig函数配置目标扇区关闭或打开写保护;

        调用FLASH_OB_Launch函数把寄存器的配置写入到选项字节;

        调用FLASH_OB_GetWRP函数检查是否配置成功;

        调用FLASH_OB_Lock禁止修改选项字节。

    配置PCROP保护

    配置PCROP保护的过程与配置写保护过程稍有区别,见代码清单 515。

    代码清单 515 配置PCROP保护(internalFlash_reset.c文件)

    1

    2 /**

    3 * @brief SetPCROP,设置PCROP位,用于测试解锁

    4 * @note 使用有问题的串口ISP下载软件,可能会导致PCROP位置1,

    5 导致无法给芯片下载程序到FLASH,本函数用于把PCROP位置1,

    6 模拟出无法下载程序到FLASH的环境,以便用于解锁的程序调试。

    7 若不了解PCROP位的作用,请不要执行此函数!!

    8 * @param None

    9 * @retval None

    10 */

    11 void SetPCROP(void)

    12 {

    13

    14 FLASH_Status status = FLASH_COMPLETE;

    15

    17

    18 FLASH_INFO();

    19 FLASH_INFO("正在设置PCROP保护,请耐心等待...");

    20 //选项字节解锁

    21 FLASH_OB_Unlock();

    22

    23 //设置为PCROP模式

    24 FLASH_OB_PCROPSelectionConfig(OB_PcROP_Enable);

    25 //设置扇区0进行PCROP保护

    26 FLASH_OB_PCROPConfig(OB_PCROP_Sector_10,ENABLE);

    27 //把寄存器设置写入到选项字节

    28 status =FLASH_OB_Launch();

    29

    30 if (status != FLASH_COMPLETE)

    31 {

    32 FLASH_INFO("设置PCROP失败!");

    33 }

    34 else

    35 {

    36 FLASH_INFO("设置PCROP成功!");

    37

    38 }

    39 //选项字节上锁

    40 FLASH_OB_Lock();

    41 }

    该代码在解锁选项字节后,调用FLASH_OB_PCROPSelectionConfig函数把SPMOD寄存器位配置为PCROP模式,接着调用FLASH_OB_PCROPConfig函数配置目标保护扇区。

    恢复选项字节为默认值

    当芯片被设置为读写保护或PCROP保护时,这时给芯片的内部FLASH下载程序时,可能会出现图 516和图 517的擦除FLASH失败的错误提示。

    图 516 擦除失败提示

    图 517 擦除进度条卡在开始状态

    只要不是把读保护配置成了级别2保护,都可以使用SRAM启动运行代码清单 516中的函数恢复选项字节为默认状态,使得FLASH下载能正常进行。

    代码清单 516 恢复选项字节为默认值

    1 // @brief OPTCR register byte 0 (Bits[7:0]) base address

    2 #define OPTCR_BYTE0_ADDRESS ((uint32_t)0x40023C14)

    3 //@brief OPTCR register byte 1 (Bits[15:8]) base address

    4 #define OPTCR_BYTE1_ADDRESS ((uint32_t)0x40023C15)

    5 //@brief OPTCR register byte 2 (Bits[23:16]) base address

    6 #define OPTCR_BYTE2_ADDRESS ((uint32_t)0x40023C16)

    7 // @brief OPTCR register byte 3 (Bits[31:24]) base address

    8 #define OPTCR_BYTE3_ADDRESS ((uint32_t)0x40023C17)

    9 // @brief OPTCR1 register byte 0 (Bits[7:0]) base address

    10 #define OPTCR1_BYTE2_ADDRESS ((uint32_t)0x40023C1A)

    11

    12 /**

    13 * @brief InternalFlash_Reset,恢复内部FLASH的默认配置

    14 * @param None

    15 * @retval None

    16 */

    17 int InternalFlash_Reset(void)

    18 {

    19 FLASH_Status status = FLASH_COMPLETE;

    20

    21 /* 使能访问选项字节寄存器 */

    22 FLASH_OB_Unlock();

    23

    24 /* 擦除用户区域 (用户区域指程序本身没有使用的空间,可以自定义)**/

    25 /* 清除各种FLASH的标志位 */

    26 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |

    27 FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);

    28

    29 FLASH_INFO("\r\n");

    30 FLASH_INFO("正在准备恢复的条件,请耐心等待...");

    31

    32 //确保把读保护级别设置为LEVEL1,以便恢复PCROP寄存器位

    33 //PCROP寄存器位从设置为0时,必须是读保护级别由LEVEL1转为LEVEL0时才有效,

    34 //否则修改无效

    35 FLASH_OB_RDPConfig(OB_RDP_Level_1);

    36

    37 status=FLASH_OB_Launch();

    38

    39 status = FLASH_WaitForLastOperation();

    40

    41 //设置为LEVEL0并恢复PCROP位

    42

    43 FLASH_INFO("\r\n");

    44 FLASH_INFO("正在擦除内部FLASH的内容,请耐心等待...");

    45

    46 //关闭PCROP模式

    47 FLASH_OB_PCROPSelectionConfig(OB_PcROP_Disable);

    48 FLASH_OB_RDPConfig(OB_RDP_Level_0);

    49

    50 status =FLASH_OB_Launch();

    51

    52 //设置其它位为默认值

    53 (*(__IO uint32_t *)(OPTCR_BYTE0_ADDRESS))=0x0FFFAAE9;

    54 (*(__IO uint16_t *)(OPTCR1_BYTE2_ADDRESS))=0x0FFF;

    55 status =FLASH_OB_Launch();

    56 if (status != FLASH_COMPLETE)

    57 {

    58 FLASH_ERROR("恢复选项字节默认值失败,错误代码:status=%X",status);

    59 }

    60 else

    61 {

    62 FLASH_INFO("恢复选项字节默认值成功!");

    63 }

    64 //禁止访问

    65 FLASH_OB_Lock();

    66

    67 return status;

    68 }

    这个函数进行了如下操作:

        调用FLASH_OB_Unlock解锁选项字节的编程;

        调用FLASH_ClearFlag函数清除所有FLASH异常状态标志;

        调用FLASH_OB_RDPConfig函数设置为读保护级别1,以便后面能正常关闭PCROP模式;

        调用FLASH_OB_Launch写入选项字节并等待读保护级别设置完毕;

        调用FLASH_OB_PCROPSelectionConfig函数关闭PCROP模式;

        调用FLASH_OB_RDPConfig函数把读保护级别降为0;

        调用FLASH_OB_Launch定稿选项字节并等待降级完毕,由于这个过程需要擦除内部FLASH的内容,等待的时间会比较长;

        直接操作寄存器,使用"(*(__IO uint32_t *)(OPTCR_BYTE0_ADDRESS))=0x0FFFAAE9;"和"(*(__IO uint16_t *)(OPTCR1_BYTE2_ADDRESS))=0x0FFF;"语句把OPTCR及OPTCR1寄存器与选项字节相关的位都恢复默认值;

        调用FLASH_OB_Launch函数等待上述配置被写入到选项字节;

        恢复选项字节为默认值操作完毕。

    main函数

    最后来看看本实验的main函数,见。代码清单 517。

    代码清单 517 main函数

    1 /**

    2 * @brief 主函数

    3 * @param 无

    4 * @retval 无

    5 */

    6 int main(void)

    7 {

    8 /* LED 端口初始化 */

    9 LED_GPIO_Config();

    10 Debug_USART_Config();

    11 LED_BLUE;

    12

    13 FLASH_INFO("本程序将会被下载到STM32的内部SRAM运行。");

    14 FLASH_INFO("下载程序前,请确认把实验板的BOOT0和BOOT1引脚都接到3.3V电源处!!");

    15

    16 FLASH_INFO("\r\n");

    17 FLASH_INFO("----这是一个STM32芯片内部FLASH解锁程序----");

    18 FLASH_INFO("程序会把芯片的内部FLASH选项字节恢复为默认值");

    19

    20

    21 #if 0 //工程调试、演示时使用,正常解除时不需要运行此函数

    22 SetPCROP(); //修改PCROP位,仿真芯片被锁无法下载程序到内部FLASH的环境

    23 #endif

    24

    25 #if 0 //工程调试、演示时使用,正常解除时不需要运行此函数

    26 WriteProtect_Test(); //修改写保护位,仿真芯片扇区被设置成写保护的的环境

    27 #endif

    28

    30

    31 /*恢复选项字节到默认值,解除保护*/

    32 if(InternalFlash_Reset()==FLASH_COMPLETE)

    33 {

    34 FLASH_INFO("选项字节恢复成功,请把BOOT0和BOOT1引脚都连接到GND,");

    35 FLASH_INFO("然后随便找一个普通的程序,下载程序到芯片的内部FLASH进行测试");

    36 LED_GREEN;

    37 }

    38 else

    39 {

    40 FLASH_INFO("选项字节恢复成功失败,请复位重试");

    41 LED_RED;

    42 }

    43

    45

    46 while (1);

    47 }

    在main函数中,主要是调用了InternalFlash_Reset函数把选项字节恢复成默认值,程序默认时没有调用SetPCROP和WriteProtect_Test函数设置写保护,若您想观察实验现象,可修改条件编译的宏,使它加入到编译中。

    3.    下载测试

    把开发板的BOOT0和BOOT1引脚都使用跳线帽连接到3.3V电源处,使它以SRAM方式启动,然后用USB线连接开发板"USB TO UART"接口跟电脑,在电脑端打开串口调试助手,把编译好的程序下载到开发板并复位运行,在串口调试助手可看到调试信息。程序运行后,请耐心等待至开发板亮绿灯或串口调试信息提示恢复完毕再给开发板断电,否则由于恢复过程被中断,芯片内部FLASH会处于保护状态。

    芯片内部FLASH处于保护状态时,可重新下载本程序到开发板以SRAM运行恢复默认配置。

    展开全文
  • 1. 现象: 调用接口: STMFLASH_Write(0x801...flash可能处于写保护的状态。 3. 解决办法: 每次写数据前先取消调写保护:调用如下接口一下,再写即可。 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_WRPRTER...
  • 前言 在嵌入式应用开发中,应用开发完成后往往需要对芯片中的程序进行加密处理,用以保护程序安全(不至被竞争对手从芯片把程序固件考走),本节将给大学介绍一个如何实现程序自动给芯片加密功能。...
  • STM32F1(Flash 读保护)

    2018-12-18 08:42:03
     在实际的产品发布中,如果不对储存在单片机 Flash 中的程序做一些保护的话,就有可能被一些不法公司,通过仿真器(J-Link,ST-Link 等)把 Flash 中的程序读取回来,得到 bin 文件或 hex 文件,然后去...
  • 功能:: 读保护设置后将不能读出flash的内容;当解除读保护的时候stm32会自动擦出整篇flash; 设置: 读保护设置: 在程序的开头加入“读保护”代码,即实现了读保护功能;(每次程序运行先 开保护)  解除读保护...
  • 在上一节中我介绍了Flash控制器时钟频率的设置,对于一般不需要解保护的Flash操作而言,设置了时钟频率就可以开始对Flash控制器的控制寄存器写入指令从而对之前我们觉得神秘莫测的Flash进行操作了。关于Flash的指令...
  • 说到STM32的FLSAH,我们的第一反应是用来装程序的,实际上,STM32的片内FLASH不仅用来装程序,还用来装芯片配置、芯片ID、自举程序等等。当然, FLASH还可以用来装数据。 自己收集了一些资料,现将这些资料总结了...
  • SPI 总线相关知识请自行百度。 STM32 SPI 状态寄存器(SPI_SR)中的一些常用标志位 TxE:发送缓冲为空 (Transmit buffer empty) ,为空则可发送数据 RXNE:接收缓冲非空 (Receive buffer not empty) 为非空则可...
  • Flash存储W25Q16芯片

    2015-03-09 15:06:18
    25Q系列比普通的串行Flash存储器更灵活,性能更优越。基于双倍/四倍的SPI,它们能够可以立即完成提供数据给RAM,包括存储声音、文本和数据。芯片支持的工作电压2.7V到3.6V,正常工作时电流小于4mA,掉电时低于1uA。...
  • SPI的通信很容易实现,相比之下,驱动FLASH反而耗费了我学习SPI整个过程的大部分时间。下面是我学习过程的一些记录。硬件平台:秉火ISO_V2开发板 实现功能:STM32使用SPI协议读写板载NOR FLASH 1. 通讯引脚  SPI...
  • 在使用MDK进行开发的时候程序编译没有问题,点击下载或仿真的时候跳出个对话框提示Flash Timeout.Reset the Target and try it again,如下图所示: 这个问题是由于在MDK的FLASH文件设置的MCU FLASH型号与使用...
  • 与并行Flash存储器相比,所需引脚少、体积小、易于扩展、与单片机或控制器连接简单、工作可靠,所以串行Flash存储器越来越多地用在各类电子产品和工业测控系统中。 DataFlash是美国Atmel公司新推出的大容量串行...
  • NANDFLASH调试(二)

    2010-10-24 20:34:00
    今天一上午测试了下nandflash的各个管脚 发现wp引脚无法拉高,一直处于写保护状态,导致NANDFLASH id无法读取,直接将wp通过上拉电阻拉高不行 将控制WP的io隔断 分别上拉和下拉电阻才搞定,具体原因不明,接下来再...
  • 单片机片内存储器烧写(ROM编程)(纯粹个人理解,收集资料总结,如有不妥还请指出)单片机应用系统由硬件和软件组成,软件的载体是硬件的程序存储器,程序存储器采用只读存储器,这种存储器在电源关闭后,仍能保存...
  • 在误操作后会锁死芯片导致不能烧录,如下图:   解决办法是用STM32 ST-LINK Utility 工具去解锁芯片: 先连接设备然后在target选项下做如下操作解锁,选择unselecet all 然后应用即可: ...
  • 51单片机——SPI

    2016-04-21 11:57:38
    单片机——SPI总线 ... SPI 是一种高速的、全双工、同步通信总线,标准的 SPI 也仅仅使用 4 个引脚,常用于单片机和 EEPROM、FLASH、实时时钟、数字信号处理器等器件的通信。 SPI 通信原理比
1 2 3 4 5 ... 20
收藏数 503
精华内容 201
关键字:

单片机flash写保护