一个关于MCU读保护操作后程序不能运行的话题
整理:MilerShao
某日,一来自北京的工程师跟我说在使用STM32L151芯片做产品开发,基本功能开发完毕。他欲通过用户程序对芯片信息块的相关选项字节进行配置,来完成芯片的读保护。结果他发现无法利用其用户程序实现芯片的读保护加密。
他把涉及芯片读保护操作的代码发给我,希望协助查看测试。我简单测试了下,发现可以实现读保加密并告知测试结果。他很快反馈说,读保护加密的确实现了,但程序没法跑了。因为忙碌,没做进一步测试,只是让他再检查下代码。
过了个周末,客户工程师还是充满忧伤和委屈继续追问此事。因为我觉得即使他自己写不来那个读保护加密代码也没关系,交给其它烧录工具来实现并没啥不好或不方便。可这哥们诉说他的经理非要它用自己的代码实现,不停追问结果,折腾好几天无果。
该工程师是利用ST官方标准固件库做的,跟我手头上的一样,都是STM32L1系列标准固件库1.3
随便搭建了个项目,并在代码里做读保护代码的编写。编译生成机器码,烧录程序进入STM32L15x所在目标板。复位目标板,发现程序真的无法运行。通过STLINK UTLITY或STVP连接目标板,发现芯片的确已经做了LEVEL 1读保护加密。
去掉源代码里相关读保护的程序代码,再次编译、烧录,程序运行一切正常。当代码里加了读保护代码后程序无法正常运行,那问题一定出在那段读保护操作的代码上。用户的代码主要就下面几句,那最有可能出问题应该在蓝色那句。该句函数调用做实质的读保护操作。
1 2 3 4 5 6 7 8 9 10 |
Rdp_Status=FLASH_OB_GetRDP(); //查询当前芯片读保护状态 if(Rdp_Status== RESET) { FLASH_Unlock(); FLASH_OB_Unlock(); FLASH_OB_RDPConfig(OB_RDP_Level_1);//做读保护配置操作 FLASH_OB_Lock(); FLASH_Lock(); FLASH_OB_Launch(); } |
打开上面蓝色语句的函数调用,其具体函数实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
FLASH_Status FLASH_OB_RDPConfig(uint8_t OB_RDP) { FLASH_Status status = FLASH_COMPLETE; uint8_t tmp1 = 0; uint32_t tmp2 = 0; /* Check the parameters */ assert_param(IS_OB_RDP(OB_RDP)); status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT); /* calculate the option byte to write */ tmp1=(uint8_t)(~(OB_RDP )); tmp2=(uint32_t)(((uint32_t)((uint32_t)(tmp1)<<16))|((uint32_t)OB_RDP)); if(status == FLASH_COMPLETE) { OB->RDP = tmp2; /* program read protection level */ } /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT); /* Return the Read protection operation Status */ return status; } |
上面代码应该说也很简单,就几条赋值语句。目的就是给读保护相关选项赋予适当的数值。这里代码有无问题就有必要结合手册相关部分和相关定义来做解读。不妨看看RDP操作相关部分。
每个OPTION项都是一个32位字,高16位与低16位必须成互补关系。其中读保护项是在上面的第一个选项字中配置的,除了 RDP和nRDP外,还有SPRMOD和nSPROMOD,他们共同组成一个完整的、可互补校验的配置字。
结合上面的原则再过来看上面提到的读保护配置函数:
1 |
FLASH_OB_RDPConfig( uint8_t OB_RDP); |
这里OB_RDP为0xBB,目的是为了实现LEVEL 1读保护加密。 当按照如下代码换算后:
1 2 3 4 5 |
tmp1 = (uint8_t)(~(OB_RDP )); tmp2=(uint32_t)(((uint32_t)((uint32_t)(tmp1)<<16))|((uint32_t)OB_RDP)); OB->RDP = tmp2; /* program read protection level */ |
Tmp2的结果是0x004400BB,然后把它写给RDP所在的选项字。这样写显然不符合上面的约定:选项字的高16位与低16位应该成互补关系。即使这样强行写进去,校验还是会出错。这应该就是执行该操作程序无法运行的原因。
将上面代码的两个地方简单修改下,其它不动。
1 2 |
uint8_t tmp1 = 0; ==> uint16_t tmp1 = 0; tmp1 = (uint8_t)(~(OB_RDP )); ==> tmp1 =(~((uint16_t)(OB_RDP ))); |
按照上面描述修改后,赋给RDP选项字的值便是0xFF4400BB.将修改过的代码重新烧录。断电、复位等都不出现死机现象,程序运行正常,利用相关工具查看芯片证实已经做了LEVEL 1级别读保护。
看来导致上面问题的原因可以归结于ST官方参考库函数的问题,是个BUG.不过结合芯片参考手册还是可以发现和调整的。后来我去ST官网找了下有无关于SMT32L15X系列的最新固件库,发现了1.31版本,比上面提到的版本新一点,特地查看了新版本的相关函数,新版在这个地方已经做了更正。
他们修改时跟我上面提到的有些差异,是因为考虑到做RDP调整时不要影响当前各SECTOR读写保护状态的设置。
所以在利用库函数编程遇到某些感觉很可能非自己软件代码原因导致MCU功能异常时,不妨点进相关库函数结合技术手册仔细查看确认下,或者找找有无更新版本的固件库。虽说ST官方库做得相当不错了,但BUG或多或少都存在。建议刚着手开发时,尽可能下载最新版本的固件库和相关技术资料。
[v_notice]扫一扫:关注微信公众号:[/v_notice]