Part1开发平台
野火STM32H750Pro开发板,QSPI双片W25Q256,原理图如下
参考资料:百度搜索datasheet5,进入该网站搜芯片型号W25Q256即可获取PDF。简要说明一下就是单个W25Q256使用按页(256字节一页)编程写入,按扇区(4KB)、块(64KB或32KB)、或整片来进行擦除。
Part2 搭建工程
首先按照这篇博客中的方法搭建工程:
(66条消息) stm32cubemx无法生成工程_工程师笔记 | 通过 STM32CubeMX制作外部Flash的烧写驱动 (.stdlr)…_瓷tun的博客-CSDN博客
这里我的工程结构与该文章保持一致,但是由于用的开发板和博主的不一样,FLASH芯片、模式也不一样,因此驱动那一块我还是按照开发板的例程来移植,不过main函数的执行流程还是可以借鉴的。
QSPI驱动可自行移步野火官方资料下载中心获取。
Part3 调试过程中出现的问题——FLASH读写失败
第一次测试,main函数代码执行步骤为:
1.数组编号 0~FF的循环
2.擦除块,并写入
3.配置为直接映射
4.打开Memory监测
监测结果如下,0x90000000~0x900001FF 数据写入失败仍然为FF
之后的数据只有一片FLASH操作成功。
第二次测试,修改代码为:
1.擦除6块
2.配置为直接映射,打开Memory监测
其中块大小为0x10000,理论上0x90060000之后应该为非擦除状态
但是反而0x90060000之前的未擦掉,之后的擦掉了,也就是说FLASH的写入和擦除都未能成功
异常数据似乎没有规律
第三次测试:使用整片擦除代替上面的6块擦除,发现根本没有擦掉。而且整片擦除函数执行很快,明显不符合实际。
检查了一下cubeMX配置的工程,发现引脚有的是高速,有的是低速,修改好之后发现还是不行。
第四次测试:直接将工程修改为单个FLASH的模式,发现整片擦除不成功,只能块擦除,查看FLASH的3个寄存器,发现在reg1中BP3 BP2 BP1 BP0为0001,也就是说使能了某种保护机制,FLASH中有一小段无法被改动。抱着试一试的想法,向寄存器reg1写入全0之后,整片擦除函数得以运行。因此怀疑前三次FLASH的操作失败是由于两片FLASH均受到了保护机制影响。
第五次测试:将工程改回双FLASH模式,读取寄存器,发现两片FLASH reg1 为0x06fe 。也就是FLASH1 的 BP3 BP2 BP1 BP0为0001(不知道为啥,第四次明明改成全0了,现在又恢复了); FLASH2的 SRP TB BP 3 BP 2 BP 1 BP 0 WEL BUSY分别为11111110,也就是说除了BUSY为0其他全是1。根据W25Q256芯片手册,FLASH2处于硬件WP引脚有效的状态,即当WP引脚为高电平时,才能对寄存器进行改写,以及设置写使能,并且保护范围是整片。尝试了一下写全0,发现只有FLASH1可以改写寄存器,果然FLASH2还是受到WP引脚影响啊。
尝试了在初始化的时候将WP引脚设置成上拉,发现没有用。直接取消GPIO初始化复用QSPI引脚,初始化为普通输出IO,然后在写寄存器为全0之前,使用 HAL_GPIO_WritePin 将该引脚拉高,结果OK,寄存器被成功改写。成功解决。
再次测试写64MB数据,结果又失败了…重新Debug发现FLASH1的局部保护又被打开了。总结前面几次得到一个结论:如果操作不当,或者代码调试还有问题,那么有可能会触发芯片的保护机制,调试过程中难免的
调整宏定义大小:
#define W25Q256JV_FLASH_SIZE 0x4000000 /* 双片 64MB */
#define W25Q256JV_BLOCK_SIZE 0x20000 /*单片64KBytes 256页为1个block,两片则为128KB*/
#define W25Q256JV_BLOCK_COUNT 512 /* 512 个block */
#define W25Q256JV_SECTOR_SIZE 0x2000 /* 4KBytes *2*/
#define W25Q256JV_PAGE_SIZE 0x200 /* 1页256*2字节 */
最终在单步调试的情况下得以解决
但是全速运行会出现有些Block只有一片FLASH成功,猜测应该是等待FLASH操作完成的判断写得不对,修改等待代码
void QSPI_FLASH_Wait_Busy(void)
{
uint32_t _reg;
while(1){
_reg = QSPI_FLASH_ReadStatusReg(1);
if((_reg & 0x0101)==0) break;
}
}
将等待插入到FLASH操作之前,最终解决。
最后检查一下边界,即最多可以读写多少字节。
发现0x90000000~0x93FFFFFF 共64MB空间读写没问题。
Part4 main代码
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_QUADSPI_Init();
/* USER CODE BEGIN 2 */
uint8_t buffer_test[0x10000] = {0};/*按半个block来定义数组大小,否则占用内存太大,引起无法调试*/
uint8_t readbuf[128] = {0};
uint32_t var = 0;
BSP_QSPI_Init();
uint32_t reg[3] = {0};
// HAL_GPIO_WritePin(GPIOG, GPIO_PIN_9, 1);
QSPI_FLASH_WriteStatusReg(1,0);
reg[0] = QSPI_FLASH_ReadStatusReg(1);
reg[1] = QSPI_FLASH_ReadStatusReg(2);
reg[2] = QSPI_FLASH_ReadStatusReg(3);
var = QSPI_FLASH_ReadDeviceID();//读ID试试
var = QSPI_FLASH_ReadID();
BSP_QSPI_Read(readbuf,0,128);
//初始化数组为
for (var = 0; var < 0x10000; var++) {
buffer_test[var] = (var & 0xff);
}
for (var = 0; var < W25Q256JV_BLOCK_COUNT; var++) {
//扇区擦除
BSP_QSPI_Erase_Block64(var * W25Q256JV_BLOCK_SIZE);
QSPI_FLASH_Wait_Busy();
//扇区写入
BSP_QSPI_Write(buffer_test,var * W25Q256JV_BLOCK_SIZE,0x10000);//先写半个block
QSPI_FLASH_Wait_Busy();
BSP_QSPI_Write(buffer_test,var * W25Q256JV_BLOCK_SIZE + 0x10000,0x10000);//再写半个block
QSPI_FLASH_Wait_Busy();
}
if (QSPI_EnableMemoryMappedMode() != QSPI_OK) {
while (1){
}
}
//比较内容
for (var = 0; var < W25Q256JV_BLOCK_COUNT; var++) {
if (memcmp(buffer_test,(uint8_t*) (0x90000000 + var * W25Q256JV_SECTOR_SIZE),
W25Q256JV_SECTOR_SIZE) != HAL_OK) {
while (1);
}
}
/* USER CODE END 2 */
while (1)
{
}
}