项目场景:
项目中原来使用华邦W25Q128芯片,后因为flash容量续替换为W25Q256芯片。Bootloader使用自己分装的对W25Q128驱动程序,APP程序使用armink提供SFUD框架。项目程序放在stm32片内flash中,片外norflash中存放OTA的固件和更新标志位。Bootloader上电读取flash中更新标志位位和校验数据都正常,APP读取flash中工作参数也都正常。
问题描述
执行完固件升级后,软重启mcu依然使用内部flash旧固件,但是断电重启后可正常加载升级后的程序。
原因分析:
先仿真APP程序,读取download区代码,发现固件升级标志位和固件描述头部信息crc都正确。再仿真Bootloader程序,发现无法读取flash中数据发现都是0xFF,片外flash未写入状态。初步怀疑是bootloader驱动问题,但是偶然发现断电重启后可以正常读取片外flash的数据,且crc正确。排除下载固件时未正确写入片外flash。
之前代码可以正常升级固件这次只是换了个固件导致代码异常。之前有个项目用到eeprom从24C32换到24C64也出现问题(可查看这篇笔记:读24cxx EEPROM),eeprom的问题是寻址从1字节变成2字节导致无法读出数据。可查看华邦W25Q256的datasheet。
W25Q256支持两种寻址模式3字节寻址
和4字节寻址
。
其区别在Address位数不同,功能码也不同3字节读数据是0x03
,4字节则是0x13
。写flash也是存在两种寻址方式
。
还有两条命令控制控制进入4字节寻址和退出4字节寻址。
那么外部flash读取失败的问题也明朗了,程序在APP中进入了4字节寻址,Bootloader中国使用的是3字节寻址。APP程序在完成固件升级后软重启MCU并不会复位W25Q256,导致flash还在4字节寻址模式中,Bootloader去读取数据只能读到0xFF。若断电后flash复位后,没进入4字节寻址boot便可以读出数据,这也就是为什么断电重启后会正常更新程序,不断电则没更新程序。
项目之前使用了SFUD框架快速开发flash,但是我们对正常运作的代码关注不够细致才导致这个bug的存在。回过去看一下SFUD是怎么实现对W25Q128和W25Q256关于寻址方面的配置。
SFUD框架中对4字节设置API函数源码:
/**
* enable or disable 4-Byte addressing for flash
*
* @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB (256Mb).
*
* @param flash flash device
* @param enabled true: enable false: disable
*
* @return result
*/
static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled) {
sfud_err result = SFUD_SUCCESS;
uint8_t cmd;
SFUD_ASSERT(flash);
/* set the flash write enable */
result = set_write_enabled(flash, true);
if (result != SFUD_SUCCESS) {
return result;
}
if (enabled) {
cmd = SFUD_CMD_ENTER_4B_ADDRESS_MODE;
} else {
cmd = SFUD_CMD_EXIT_4B_ADDRESS_MODE;
}
result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
if (result == SFUD_SUCCESS) {
flash->addr_in_4_byte = enabled ? true : false;
SFUD_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit");
} else {
SFUD_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit");
}
return result;
}
其函数调用是根据flash芯片大小判断的:(源码中注释写的16MB应该是注释标注错误,256Mb是32MB)
/* if the flash is large than 32MB (256Mb) then enter in 4-Byte addressing mode */
if (flash->chip.capacity > (1L << 24)) {
result = set_4_byte_address_mode(flash, true);
} else {
flash->addr_in_4_byte = false;
}
解决方案:
其实出现这个问题往往是因为更换硬件芯片,只对现有代码运行一下发现能用就没有继续思考下去。加上boot程序往往代代相传,当初没有考虑到不同芯片的兼容问题,也可能是为了压缩程序空间故意没做兼容。
可以有两种解决方案:
方案1:对于已经出货的机器可以修改APP程序,在OTA下载完固件后,软重启前退出4字节模式,这样Bootloader可以正常读取更新标志位。个人觉得若出现这种出货的情况数据项目过程中风险评估不到位加上测试不到位。
方案2:修改boot程序,增加对机型判断若是256Mb的芯片使用4字节模式寻址读写数据。