在cubemx下操作flash
使用最小系统板,最小系统板的flash为SPI2,具体管脚对应如下:
PB15–>SPI2_MOSI
PB14–>SPI2_MISO
PB13–>SPI2_SCK
PB12–>GPIO_Output(片选)
在cube中设置SPI2模式为全双工模式,禁止硬件NSS片选,如果使用NSS片选可能会出现问题(没有详细试验过)。SPI速率采用18.0MBits/s,SPI设置为模式0,模式3也行。
至此,对SPI的配置完成。
读取厂家及ID信息
首先对于W25Q64的操作可以从时序图入手,以对Manufacturer / Device ID的操作为例。
由上图可知,要读取设备信息先要在CS为低电平期间向flash通过IO0发送指令0x90,然后发送24bits地址0x00,在时钟不间断的情况下IO1返回Manufacturer / Device ID。
void W25Qx_Read_ID(uint16_t *ID)
{
uint8_t idt[2];
uint8_t cmd[4] = {
READ_ID_CMD,0x00,0x00,0x00};
W25Qx_Enable();
/* Send the read ID command */
HAL_SPI_Transmit(&hspi2, cmd, 4, W25QXXXX_TIMEOUT_VALUE);
/* Reception of the data */
HAL_SPI_Receive(&hspi2,idt, 2, W25QXXXX_TIMEOUT_VALUE);
*ID = (idt[0] << 8) + idt[1];
W25Qx_Disable();
}
对于读写等操作类似。
具体编程思路
flash在读写等操作时可能需要较长时间才能完成,在操作进行期间,设备处于忙的状态,该状态可以通过对状态寄存器中位的读取进行判断。当设备忙时,等待,超出等待时间,拉高CS,结束操作。
/* Wait the end of Flash writing */
while(W25Qx_GetStatus() == W25Qx_BUSY)
{
/* Check for the Timeout */
if((get_tick() - tickstart) > W25QXXXX_TIMEOUT_VALUE)
{
return W25Qx_TIMEOUT;
}
delay(1);
}
return W25Qx_OK;
读操作
和读ID一样,先发送指令,再发送地址,接着返回数据,不同的是,只要CS一直处于低电平期间,只要时钟一直有,则数据会一直往下读出。
uint8_t W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
{
uint8_t cmd[4];
/* Configure the command */
cmd[0] = READ_CMD;
cmd[1] = (uint8_t)(ReadAddr >> 16);
cmd[2] = (uint8_t)(ReadAddr >> 8);
cmd[3] = (uint8_t)(ReadAddr);
W25Qx_Enable();
/* Send the read ID command */
HAL_SPI_Transmit(&hspi2, cmd, 4, W25QXXXX_TIMEOUT_VALUE);
/* Reception of the data */
if (HAL_SPI_Receive(&hspi2, pData,Size,W25QXXXX_TIMEOUT_VALUE) != HAL_OK)
{
return W25Qx_ERROR;
}
W25Qx_Disable();
return W25Qx_OK;
}
写操作
在写操作开始之前先要写入write enable指令,然后再开始写。首先需要根据要写的起始地址判断该地址处于哪一个page、sector和block。然后再根据要写入的数据大小判断需要对哪些区域进行擦除(flash写入之前必须处于擦除状态,写入只能将单元中的内容从1编程0,不能逆向操作)。擦除完后进行写入,每次最多写入1个page,即256bytes。
uint8_t W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
{
uint8_t cmd[4];
uint32_t end_addr, current_size, current_addr;
uint32_t tickstart = get_tick();
/* Calculation of the size between the write address and the end of the page */
current_addr = 0;
while (current_addr <= WriteAddr)
{
current_addr += W25QXXXX_PAGE_SIZE;
}
current_size = current_addr - WriteAddr;
/* Check if the size of the data is less than the remaining place in the page */
if (current_size > Size)
{
current_size = Size;
}
/* Initialize the adress variables */
current_addr = WriteAddr;
end_addr = WriteAddr + Size;
/* Perform the write page by page */
do
{
/* Configure the command */
cmd[0] = PAGE_PROG_CMD;
cmd[1] = (uint8_t)(current_addr >> 16);
cmd[2] = (uint8_t)(current_addr >> 8);
cmd[3] = (uint8_t)(current_addr);
/* Enable write operations */
W25Qx_WriteEnable();
W25Qx_Enable();
/* Send the command */
if (HAL_SPI_Transmit(&hspi2,cmd, 4