如何使用HAL API编程STM32选项字节
尽管可以使用STM32CubeProgramer等工具通过调试器对选项字节进行编程,但在许多情况下,在应用程序的运行时对选项字节编程是必要的或有帮助的。STM32 HAL库。。。
尽管可以使用STM32CubeProgramer等工具通过调试器对选项字节进行编程,但在许多情况下,在应用程序的运行时对选项字节编程是必要的或有帮助的。STM32 HAL库提供了一个API,用于编程和检查应用程序代码中的选项字节。本文的目的是展示HAL选项字节API在多个用例中的正确用法。
FLASH_OBProgramInitTypeDef结构包含编程和读取选项字节所需的所有信息。使用OB.USERConfig,您可以检查OPTR寄存器的值,它将显示用户选项字节的当前值。在不必要地解锁闪存和选项字节之前,您总是希望检查选项字节是否已处于所需配置中。另一件需要注意的事情是HAL_FLASH_OB_Launch()会导致系统重置,因此永远不会返回。这就是HAL_ERROR的原因。
在启动功能后返回,因为它永远不应该到达。
首先,我们检查是否有任何一个所需的选项配置不正确。然后,将OB.USERType设为要编程的用户选项的组合,将OB.USERConfig设为这些选项的值的组合。
写入保护的起始值和结束值以闪存页码为单位。例如,ZoneA_Start=0和ZoneA_End=4表示前5页是写保护的。拥有这些值有助于了解当前的写保护是什么,或者它是否被激活。当开始大于结束时,意味着该区域没有激活的写保护。
以下是设置WRP的示例功能:
以下是设置PCROP的示例功能:
尽管上面的例子没有这样做,但建议的最佳实践是,鉴于特定MCU的闪存大小,您的函数会检查地址是否有效,因为HAL OB程序函数不会绑定检查输入。
如上所述,PCROP的粒度大于单个地址,但尽管如此,传递给API的地址将接受任何单个地址。因此,在选项字节API内,由于MCU的PCROP的粒度,地址被截断:
ropbase是闪存的开始:0x8000000。例如,假设PCROP的粒度为512字节,让我们使用上面的setPCROP()函数和一些任意值:
以下是截断过程中发生的情况:
这产生了进入PCROP寄存器的512字节块偏移,其在地址空间方面对应于这些实际保护区域:
https://community.st.com/s/article/What-Are-Option-Bytes-In-Stm32-And-How-Do-I-Use-Them
1.用户选项字节
1.1修改单个用户选项
以下是对用户选项字节进行编程的示例。在这种情况下,下面的函数会清除nBOOT_SEL选项位。它使用HAL状态typedefs对函数进行错误检查。HAL_StatusTypeDef ClearnBootSel(){FLASH_OBProgramInitTypeDef OB;HAL_FLASHEx_OBGetConfig(&OB);/*OB.USERConfig返回FLASH_OPTR寄存器*///如果(OB.USERConfig&FLASH_OPTR_nBOOT_SEL){HAL_FLASH_Unlock();HAL_FLASH_OB_Unlock();OB.OptionType=OPTIONBYTE_USER;OB.USERType=OB_USER_nBOOT_SEL;OB.USERConfig=OB_BOOT0_FROM_IN;if(HAL_FLASHEx_OB程序(&OB)!=HAL_OK){HAL_FLASH_OB_Lock();HAL_FLASH-Lock();返回HAL_ERROR;}HAL_FLASH_OB_Launch();/*我们不应该让它通过Launch,所以锁定*闪存并从函数*/HAL_flash_OB_lock()返回一个错误;HAL_FLASH_Lock();返回HAL_ERROR;}返回HAL_OK;}
FLASH_OBProgramInitTypeDef结构包含编程和读取选项字节所需的所有信息。使用OB.USERConfig,您可以检查OPTR寄存器的值,它将显示用户选项字节的当前值。在不必要地解锁闪存和选项字节之前,您总是希望检查选项字节是否已处于所需配置中。另一件需要注意的事情是HAL_FLASH_OB_Launch()会导致系统重置,因此永远不会返回。这就是HAL_ERROR的原因。
在启动功能后返回,因为它永远不应该到达。
1.2同时修改多个用户选项
还可以同时对多个USER选项字节进行编程。这可以通过组合选项字节API的定义来实现。例如,您希望将nRST_STOP、nRST_STDBY和nRST_SHDW分别修改为1、0和1:HAL_StatusTypeDef modifyRST(){FLASH_OBProgramInitTypeDef OB;HAL_FLASHEx_OBGetConfig(&OB);//检查是否清除了(;OB.OptionType=选项BYTE_USER;OB.USERType=OB_USER_nRST_STOP|OB_USER_nRST_STDBY|OB_USER_nRST_SHDW;OB.USERConfig=OB_STOP_NORST|OB_STANDBY_RST|OB_SHUTDOWN_NORST;如果(HAL_FLASHEx_OBProgram(&OB)!=HAL_OK){HAL_FLASH_OB_Lock();HAL_FLASH-Lock();返回HAL_ERROR;}HAL_FLASH_OB_Launch();/*我们不应该让它通过Launch,所以锁定*闪存并从函数*/HAL_flash_OB_lock()返回一个错误;HAL_FLASH_Lock();返回HAL_ERROR;}返回HAL_OK;}
首先,我们检查是否有任何一个所需的选项配置不正确。然后,将OB.USERType设为要编程的用户选项的组合,将OB.USERConfig设为这些选项的值的组合。
2.设置RDP级别
修改RDP级别类似于修改USER选项,但有点简单,因为API结构提供了专门针对RDP编程的成员:HAL_StatusTypeDef SetRDPLevel1(){FLASH_OBPProgramInitTypeDef OB;HAL_FLASHEx_OBGetConfig(&OB我们不应该让它通过Launch,所以锁定*闪存并从函数*/HAL_flash_OB_lock()返回一个错误;HAL_FLASH_Lock();返回HAL_ERROR;}返回HAL_OK;}
3.与WRP合作
3.1基于区域的WRP
在一些MCU系列上,例如G和L系列,WRP被设置为两个闪存页之间的区域(WRP区域包括开始和结束闪存页)。以下是在此类设备上获得电流保护的示例:void getWriteProtections(uint8_t*ZoneA_Start,uint8_t*ZONE_End,uint8-t*ZoneB_Start,uind8_t*ZoneB_End){FLASH_OBPProgramInitTypeDef OB;OB.WRPArea=OB_WRPArea_ZONE_A;HAL_FLASHEx_OBGetConfig(&OB);*ZoneA_Start=OB.WRPStartOffset RPStartOffset;*ZoneB_End=OB.WRPEndOffset;}
写入保护的起始值和结束值以闪存页码为单位。例如,ZoneA_Start=0和ZoneA_End=4表示前5页是写保护的。拥有这些值有助于了解当前的写保护是什么,或者它是否被激活。当开始大于结束时,意味着该区域没有激活的写保护。
uint8_t a_beg,a_end,b_beg,b_end;getWriteProtections(&a_beg、&a_end、&b_beg、&b_end);if((a_beg>a_end)&&(b_beg>b_end)){//没有激活的写保护}
以下是设置WRP的示例功能:
HAL_StatuTypeDef setWRP(uint8_t A_Start,uint8_t A_End,uint8 _t B_Start,uint 8_t B_End);返回HAL_ERROR;}OB.WRPArea=OB_WRPAREA_ZONE_B;OB.WRPStartOffset=B_Start;OB.WRPEndOffset=B_End;如果(HAL_FLASHEx_OBProgram(&OB)!=HAL_OK){HAL_FLASH_OB_Lock();HAL_FLASH-Lock();返回HAL_ERROR;}HAL_FLASH_OB_Launch();/*我们不应该让它通过Launch,所以锁定*闪存并从函数*/HAL_flash_OB_lock()返回一个错误;HAL_FLASH_Lock();返回HAL_ERROR;}
3.2基于行业的WRP
某些零件具有按扇区启用或禁用的WRP,而不是定义为两个扇区之间的范围,例如H7系列。以下是更改H743上的WRP的示例:
HAL_StatusTypeDef ChangeWRP(uint32_t银行_num,uint32_t状态,uint22_t扇区){FLASH_OBProgramInitTypeDef OB;OB.OptionType=OPTIONBYTE_WRP;OB.WRPState=状态;OB.Banks=银行_num;OB.WRP扇区=扇区;HAL_FLASH_Unlock();HAL_FLASH_OB_Unlock();if(HAL_FLASHEx_OBProgram(&OB)!=HAL_OK)不应达到HAL_FLASH_OB_Lock();HAL_FLASH_Lock();return HAL_ERROR;}…………//在银行1的扇区1、3、5上启用WRP的示例。uint32_t扇区=OB_WRP_SECTOR_1|OB_WRP_SECTOR_3|OB_WRP-_SECTOR_5;更改WRP(FLASH_BANK_1,OB_WRPSTATE_ENABLE,扇区);
4.与PCROP合作
使用PCROP将类似于使用WRP,但该结构将保护作为选项字节结构中的地址提供。以下是读取当前PCROP值的示例:void getPCROP(uint32_t*分区起始,uint32.t*分区结束,uint22_t*分区开始,uint332_t*分区结束){FLASH_OBProgramInitTypeDef OB;HAL_FLASHEx_OBGetConfig(&OB);*分区起始=OB.PCROP1AStartAddr;*分区结束=OB.PCROP1AEndAddr;*Zone_Start=OB.PCROP1BStartAddr,*Zone_End=OB.PROP1BEndAddr就像WRP一样,您可以使用PCROP地址来知道它是否被禁用。如果该区域的起始地址大于结束地址,则禁用PCROP。然而,与WRP API不同,PCROP值由结构作为实际地址给出,即使PCROP选项字节本身将地址表示为较低粒度块的偏移量。
以下是设置PCROP的示例功能:
HAL_StatusTypeDef setPCROP(uint32_t A_Start,uint32_t A_End,uint22_t B_Start,uint32_t B_End结束;如果(HAL_FLASHEx_OBProgram(&OB)!=HAL_OK){HAL_FLASH_OB_Lock();HAL_FLASH-Lock();返回HAL_ERROR;}HAL_FLASH_OB_Launch();/*我们不应该让它通过Launch,所以锁定*闪存并从函数*/HAL_flash_OB_lock()返回一个错误;HAL_FLASH_Lock();返回HAL_ERROR;}
尽管上面的例子没有这样做,但建议的最佳实践是,鉴于特定MCU的闪存大小,您的函数会检查地址是否有效,因为HAL OB程序函数不会绑定检查输入。
如上所述,PCROP的粒度大于单个地址,但尽管如此,传递给API的地址将接受任何单个地址。因此,在选项字节API内,由于MCU的PCROP的粒度,地址被截断:

ropbase是闪存的开始:0x8000000。例如,假设PCROP的粒度为512字节,让我们使用上面的setPCROP()函数和一些任意值:
uint32_t分区_beg=0x8000473;uint32_t分区_end=0x8000891;uint32_t分区_beg=0x8000c35;uint32_t分区_end=0x8000f42;设置PCROP(区域_beg、区域_end、区域_beg和区域_end);
以下是截断过程中发生的情况:
//右移9,因为512=1<<9 0x8000473-0x8000000=0x473>>9=2 0x8000891-0x80000 00=0x891>>9=4 0x8000c35-0x8000000=0xc35>>9=6 0x8000f42-0x8000000/0xf42>>9=7
这产生了进入PCROP寄存器的512字节块偏移,其在地址空间方面对应于这些实际保护区域:
//512字节=0x200 ZoneB_Start=0x8000000+(0x200*2)=0x8000400//整个子部门受到保护Zone_End=0x8000000+(0x200*4,该扇区的末尾为0x8000FFF总体:区域A=0x8000400至0x80009FF(含)区域B=0x8000C00至0x8000FFF(含)
5.结论
尽管本文没有涉及选项字节的每个可能的用例,但这些示例可以作为一个起点,帮助您了解如何使用HAL选项字节API。根据您的特定MCU,在选项字节实现方面可能存在微小差异。有关选项字节的更多详细信息,请始终参阅该部件的数据表和参考手册。如果你想了解更多关于字节选项的信息,请参阅本文:https://community.st.com/s/article/What-Are-Option-Bytes-In-Stm32-And-How-Do-I-Use-Them