本帖最后由 凉白开 于 2022-6-15 12:50 编辑
问题描述:
在调试过程中发现有些产品烧录过一次以后就不能再次烧录了,但此时程序功能又能正常运行,所以我们推测芯片应该没有挂掉,可能只是flash无法烧录了。于是我们写了一个测试代码加载到flash中查看flash的状态,结果发现是flash的读保护功能莫名奇妙的打开了。我们的应用需要配置选项字节将PC6配置为PWM功能,除此之外不会有其他操作碰到读保护的相关代码。于是我们就对这段代码进行了分析。代码如下 问题代码: void AFConfig(void)
{
FMC_AFRConfig_T AFRConfig;
/** Set PC6 alternate function = TMR1_CH1 */
AFRConfig.AFR0 = FMC_OB_AFR_ENABLE;
AFRConfig.AFR1 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR3 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR4 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR5 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR7 = FMC_OB_AFR_DISABLE;
FMC_Unlock();
FMC_EraseOptionByte();
FMC_ConfigOptionByteAFR(&AFRConfig);
FMC_Lock();
}
FMC_STATE_T FMC_EraseOptionByte(void)
{
uint16_t rpKey;
FMC_STATE_T state;
/******第 1 步******/
rpKey = FMC->OBCS_B.READPROT ? 0 : FMC_RP_KEY;
state = FMC_WaitForReady(FMC_DELAY_ERASE);
if(state == FMC_STATE_COMPLETE)
{
/******第 2 步******/
FMC->OBKEY = FMC_KEY_1;
FMC->OBKEY = FMC_KEY_2;
FMC->CTRL2_B.OBE = BIT_SET;
FMC->CTRL2_B.STA = BIT_SET;
state = FMC_WaitForReady(FMC_DELAY_ERASE);
if(state == FMC_STATE_COMPLETE)
{
FMC->CTRL2_B.OBE = BIT_RESET;
FMC->CTRL2_B.OBP = BIT_SET;
/******第 3 步******/
OB->READPROT = rpKey;
state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
if(state != FMC_STATE_TIMEOUT)
{
FMC->CTRL2_B.OBP = BIT_RESET;
}
}
else if(state != FMC_STATE_TIMEOUT)
{
FMC->CTRL2_B.OBE = BIT_RESET;
}
}
return state;
}
代码分析: 这里我们直接看跟“读保护”相关的代码: 第一步:先判断了当前芯片是否处于读保护的状态,如果处于读保护状态则rpkey=0,如果不处于读保护状态则rpkey= FMC_RP_KEY, 第二步:擦除选项字节。 第三步:将rpkey写入选项字节。 关键问题在第三步,查看数据手册我们可以看到OB->READPROT为0x5a时表示FLASH读保护处于取消状态,OB->READPROT为其他值时,读保护就会处于使能状态。如果我们在执行第二步时,芯片因为异常断电之类的原因,无法执行第三步的写入rpkey操作,则会导致读保护异常使能的情况。 解决办法: 根据前面的分析,我们需要保证芯片在操作选项字节时不会死机才能确保读保护不会错误使能。但这个条件在实际使用过程中是很难满足的,最常见的异常断电就很难避免,我们只能尽可能的规避。下面贴出我的解决办法: void AFConfig(void)
{
FMC_AFRConfig_T AFRConfig;
/** Set PC6 alternate function = TMR1_CH1 */
AFRConfig.AFR0 = FMC_OB_AFR_ENABLE;
AFRConfig.AFR1 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR3 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR4 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR5 = FMC_OB_AFR_DISABLE;
AFRConfig.AFR7 = FMC_OB_AFR_DISABLE;
AFRConfig.RESERVED1 = 0;
AFRConfig.RESERVED2 = 0;
uint8_t afr_byte_w = *(uint8_t *)&AFRConfig;
afr_byte_w = ~afr_byte_w;
uint8_t afr_byte_r = OB->AFR;
if(afr_byte_w != afr_byte_r)
{
FMC_Unlock();
FMC_EraseOptionByte();
FMC_ConfigOptionByteAFR(&AFRConfig);
FMC_Lock();
}
}
在操作选项字节之前,先判断当前的选项字节是否等于我要设置的内容,如果相等,那么我就不去设置选项字节了,这样就减少了写入选项字节的概率。因为选项字节是类似flash的器件,是有掉电保存功能的,所以通常情况下,我们只会在第一次上电的时候操作到选项字节,这样也就大大的降低了触发错误的概率。
|