如何利用AS32系列MCU芯片使用简洁单线模式操作QSPI FLASH?

QSPI(Quad Serial Peripheral Interface) 是一种高速串行通信接口,在标准SPI(Serial Peripheral Interface)的基础上扩展至4条数据线(Quad Mode),显著提升数据传输速率。它广泛应用于Flash存储器、传感器和微控制器之间的通信。AS32系列MCU芯片开发板集成了一块S25FL512SAGMFVG13型号的QSPI FLASH,本文我们将介绍该系列芯片使用简洁单线模式操作QSPI FLASH。

代码详解:

1、扇区擦除函数

根据指导手册我们知道,在进行扇区擦除命令之前必须先给一个WREN 命令,该命令会将状态寄存器中的写使能锁存器(WEL)置位,以允许任何写入操作。此外,本文用例使用3字节地址,根据时序图可知,应先发送指令(长度可配置)接着发送一个24位地址。

我们开始讲解扇区擦除函数,代码如下:

#define WRITE_ENABLE_INST 0x06

#define WRITE_DISBALE_INST 0x04

#define ERASE_SECTOR_INST 0xD8

#define READ_RIGISTER1_INST 0x05

#define PAGE_PROGRAME_INST 0x02

#define READ_FLASH_INST 0x03
void QSPI_FlashErase(uint32_t block_start, uint32_t sector_num)

{

uint32_t dst;

uint32_t SR_Status;

uint8_t erase_status = 1;

dst = block_start;

uint32_t num = sector_num-1;

if(dst + num * 0x3FFFF> 0xFFFFFF)

{

Printf("error :Exceeds the erase range.\r\n");

}

QSPI_Reset();

QSPI_Deinit();

QSPI_StructInitenable();

QSPI_WriteEnable();

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

for(uint32_t i = 0; i < num; i++)

{

QSPI_SectorErase();

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

QSPI_SetAddr(dst + num*0x3FFFF);

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

}

while(erase_status)

{

QSPI_SetDataLength(0);

QSPI_ReadStatus_Rigister1();

delay_ms(10);

erase_status = QSPI_ReadData();

}

QSPI_ReadFlash(dst, PROGRAME_SIZE, QSPI_BUFFER );

}

首先看第16行,对QSPI结构体初始化,分别配置片选高电平时间,QSPI时钟信号在指令之间的电平,QSPI时钟分频。

接着第17行,发送写使能命令。该函数主要配置QSPI->CCR寄存器,主要配置指令写使能指令和简介写模式,其余配置读者可查阅AS32X601设计手册的QSPI章节,这里不做过多赘述。

第22行,进行扇区擦除,和写使能函数一致,区别在于配置不同的指令,第24行设置需要擦除区域的首地址。

第30行,配置QSPI数据长度寄存器(DLR)为0,我们需要读取1字节,即SR1寄存器8位。第31行,我们发送读取QSPI FLASH的SR1寄存器指令。第33行,读取QSPI FLASH返回的8位数据,直到erase_status = 0时,我们擦除操作完成。这里是间接读取模式,还可使用轮询模式,减少CPU等待时间,完全交给QSPI FLASH来处理数据。

以下是读取SR1寄存器的相关配置,和擦除函数一致,区别在于指令,当然读者可以自行配置QSPI 为2线或4线。

2、读QSPI FLASH

接上节,我们在最后调用了QSPI_ReadFlash函数,本章节详细讲解该函数,先看FLASH手册,

该指令允许从存储器阵列的任意字节地址开始读取。每输出一个字节数据后,地址会自动递增至下一个更高地址(按顺序连续递增)。因此,只需提供起始地址000000h并发送一次读指令,即可连续读取整个存储器的内容。当到达最高地址后,地址计数器将自动回绕至000000h,从而支持无限循环的连续读取操作。

函数如下:

void QSPI_ReadFlash(uint32_t block_start, uint32_t byte_num, uint32_t buffer[])

{

uint32_t FIFO_LEVEL = 0;

QSPI_WriteEnable();

QSPI_SetDataLength(byte_num);

QSPI_Read_Flash();

QSPI_SetAddr(block_start);

delay_ms(10);

FIFO_LEVEL = QSPI_GetFIFOLevel();

while(FIFO_LEVEL != 0)

{

for(uint32_t i=0;i<FIFO_LEVEL;i++)

{

buffer[i] = QSPI_ReadData();

Printf("%x\t",buffer[i]);

}

FIFO_LEVEL = QSPI_GetFIFOLevel();

}

QSPI_WriteDisable();

}

同样在读数据之前需要解锁QSPI FLASH,并在操作完成后上锁。第5行,设置读取需要读取的字节数,第6行,调用QSPI_READ_FLASH(),函数如下,第9行,获取FIFO中有效字节数,当FIFO非空时,读QSPI->DR寄存器获取数据,直到FIFO为空。

3、下板验证

程序初始化后,调用QSPI_FlashErase(0x00, 1),从0地址开始,擦除1个扇区,观察打印信息,并截取了逻辑分析仪片段。

4、页编程函数

主要内容:若地址的最低9位(A8-A0)不全为0,则超出当前页末尾的传输数据将从同一页的起始地址(即最低9位全为0的地址)开始编程,即地址会在页对齐的边界内回绕。这是因为用户只需输入单个页地址即可覆盖整个页边界。

如果发送给设备的数据不足一页,这些数据字节将从所提供地址开始按顺序编程,而不会影响同一页内的其他字节。

为优化时序,使用页编程(PP)命令在页边界内加载整个页大小的编程缓冲器,相比于加载不足一页的数据到编程缓冲器,将节省总体编程时间。

int QSPI_FlashWrite(uint32_t block_start,uint32_t offset_into_block, uint32_t count, uint32_t buffer[])

{

uint32_t dst;

uint32_t ii;

uint32_t num;

dst = block_start + offset_into_block;

num = count;

if(dst%4 != 0)

{

Printf( "the addr must 4-byte alignment\r\n");

return 0;

}

QSPI_Deinit();

if((dst%256) == 0)

{ int loop = 0;

Printf( "the first packge is 256 bytes\r\n");

while(num > 256)

{

QSPI_StructInitenable();

QSPI_SetDataLength(0xff);

QSPI_WriteEnable();

QSPI_PageProgram();

QSPI_SetAddr(dst);

for(ii = loop*256/4; ii < loop*256/4+64; ii++)

{

QSPI->DR = buffer[ii];

}

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

dst += 256;

num -= 256;

delay_ms(20);

loop++;

}

QSPI_StructInitenable();

QSPI_SetDataLength(block_start + offset_into_block + count - dst -1);

QSPI_WriteEnable();

QSPI_PageProgram();

QSPI_SetAddr(dst);

for(ii = 256*loop/4; ii < (256*loop+block_start + offset_into_block + count - dst)/4; ii++)

{

QSPI->DR = buffer[ii];

}

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

dst += block_start + offset_into_block + count -- dst;

}

else

{

QSPI_StructInitenable();

QSPI_WriteEnable();

QSPI_PageProgram();

QSPI_SetAddr(dst);

QSPI_SetDataLength(256-(dst%256));

for(ii = 0; ii < (256-(dst%256))/4; ii++)

{

QSPI->DR = buffer[ii];

}

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

dst += 256-(dst%256);

while(num > 256)

{

QSPI_StructInitenable();

QSPI_WriteEnable();

QSPI_PageProgram();

QSPI_SetAddr(dst);

QSPI_SetDataLength(256-1);

for(ii = 0; ii < 64; ii++)

{

QSPI->DR = buffer[ii];

}

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

dst += 256;

num -= 256;

}

QSPI_StructInitenable();

QSPI_WriteEnable();

QSPI_PageProgram();

QSPI_SetAddr(dst);

QSPI_SetDataLength(block_start + offset_into_block + count - dst -1);

for(ii = 0; ii < (block_start + offset_into_block + count - dst)/4; ii++)

{

QSPI->DR = buffer[ii];

}

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

dst += block_start + offset_into_block + count - dst;

}

QSPI_WriteDisable();

while( (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) == 1));

QSPI_ReadFlash(block_start,PROGRAME_SIZE,QSPI_BUFFER);

return 0; //RESULT_OK

}

第1至13行定义了函数参数并计算目标地址,同时检查地址是否4字节对齐,若未对齐则报错返回。第14行对QSPI控制器进行复位。第15至48行处理目标地址256字节对齐的情况:第16至35行循环写入完整的256字节数据页,每次写入前初始化硬件、设置数据长度、使能写入、发送页编程命令并设置地址,随后通过循环写入64个32位数据,等待操作完成后更新地址和剩余计数;第36至47行处理剩余不足256字节的数据尾部,采用类似的流程但数据长度根据剩余量动态计算。第49至88行处理目标地址未256字节对齐的情况:第51至60行先写入从起始地址到下一个256字节边界的首部数据片段;第61至75行循环写入后续完整的256字节页;第76至87行处理最后的尾部数据。

5、下板验证

准备写入1024个数,0-0x3FF,共4K,从0地址开始,观察打印信息

相关推荐
iCxhust2 小时前
__acrtused 是什么
c语言·c++·单片机·嵌入式硬件·微机原理
雾岛听风眠2 小时前
【OpenCV+STM32】二维云台颜色识别及追踪
stm32·单片机·嵌入式硬件
闲人编程2 小时前
FastAPI性能优化技巧
后端·python·性能优化·fastapi·性能·codecapsule
nassi_3 小时前
ESP8266 Wi-Fi模块解析
stm32·嵌入式硬件
向阳逐梦3 小时前
马达驱动芯片核心逻辑:从信号到动力的“功率放大密码”
单片机·嵌入式硬件
1+2单片机电子设计3 小时前
基于 STM32 的羽毛球运动状态监测系统设计
stm32·单片机·嵌入式硬件
国科安芯3 小时前
CANFD 总线多节点扩展技术:节点数量限制与突破方案
单片机·嵌入式硬件·安全性测试
电子小子洋酱3 小时前
Linux驱动开发学习笔记(更新中)
linux·笔记·单片机
hid646637223 小时前
基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出回归预测python代码
安全性测试