赞
踩
上节内容学习了通过SPI读取FLASH的JEDEC_ID,在flash资料的指令表中,还看到有很多指令可以使用,这节继续学习使用其他指令,程序模板采用上节的模板。
为了方便起见,把这节需要用到的指令都可以宏定义出来:
/*FLASH 常用命令*/ #define WriteEnable 0x06 /* 写使能 */ #define WriteDisable 0x04 /* 写失能 */ #define ReadStatusReg 0x05 /* 读状态寄存器 */ #define WriteStatusReg 0x01 /* 写状态寄存器 */ #define ReadData 0x03 /* 读数据 */ #define FastReadData 0x0B /* 快速的读数据 */ #define FastReadDual 0x3B /* 双倍速快读 */ #define PageProgram 0x02 /* 页写入 */ #define BlockErase 0xD8 /* 块擦除 */ #define SectorErase 0x20 /* 扇区擦除 */ #define ChipErase 0xC7 /* 芯片擦除 */ #define PowerDown 0xB9 /* flash掉电 */ #define ReleasePowerDown 0xAB /* 掉电复位 */ #define DeviceID 0xAB /* 设备ID */ #define ManufactDeviceID 0x90 /* 制造商ID */ #define JedecDeviceID 0x9F /* JedecDeviceID */ #define sFLASH_ID 0XEF4018 /* JedecDeviceID宏定义 */ #define Dummy 0xFF /* 任意数据 */
/** * @brief Flash进入掉电模式 * @param 无 * @retval 无返回值 */ void SPI_Flash_PowerDown(void) { SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(PowerDown); /* 发送掉电信号 */ SPI_FLASH_CS_HIGH(); /* 开始通讯: CS 高电平 */ } /** * @brief 将Flash从掉电模式唤醒 * @param 无 * @retval 无返回值 */ void SPI_Flash_WakeUp() { SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(ReleasePowerDown); /* 发送掉电复位信号 */ SPI_FLASH_CS_HIGH(); /* 开始通讯: CS 高电平 */ }
/** * @brief 写使能 * @param 无 * @retval 无 */ void SPI_FLASH_Write_Enable(void) { SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(WriteEnable); /* 发送写使能信号 */ SPI_FLASH_CS_HIGH(); /* 停止通讯: CS 高电平 */ } /** * @brief 擦除数据 * @param 地址 * @retval 无返回值 */ void SPI_Flash_Erase(u32 addr) { SPI_FLASH_Write_Enable(); /* 下发指令前,先写使能 */ WateForReady(); /* 等待Flash内部时序完成,主要是读芯片的状态字 */ SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(SectorErase); /* 发送擦除指令 */ SPI_FLASH_SendByte((addr & 0xFF0000) >> 16 ); /* 取16-23位 */ SPI_FLASH_SendByte((addr & 0xFF00) >> 8); /* 取8-15位 */ SPI_FLASH_SendByte(addr & 0xFF); /* 取0-7位 */ SPI_FLASH_CS_HIGH(); /* 停止通讯: CS 高电平 */ WateForReady(); } /** * @brief 整片擦除数据 * @param 地址 * @retval 无返回值 * @attention 整片擦除时间比较耗时,具体擦除需要时间根据芯片容量大小而定 */ void SPI_Flash_BulkErasse(void) { SPI_FLASH_Write_Enable(); //写使能 SPI_FLASH_CS_LOW(); //开始通讯 SPI_FLASH_SendByte(ChipErase); //发送正片擦除指令 SPI_FLASH_CS_HIGH(); //结束通讯 } /** * @brief 读取数据 * @param pdata:读取数据缓存 addr:读取起始地址 numByteToRead:读取数据数量 * @retval 无返回值 */ void SPI_Flash_ReadDate(u8* pdata,u32 addr,u32 numByteToRead) { WateForReady(); /* 等待Flash内部时序完成,主要是读芯片的状态字 */ SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(ReadData); /* 发送读取指令 */ SPI_FLASH_SendByte((addr & 0xFF0000) >> 16 ); /* 取16-23位 */ SPI_FLASH_SendByte((addr & 0xFF00) >> 8); /* 取8-15位 */ SPI_FLASH_SendByte(addr & 0xFF); /* 取0-7位 */ while(numByteToRead--) /* 循环读取数据 */ { *pdata = SPI_FLASH_SendByte(Dummy); /* 发送Dummy任意数据,返回的数据就是读取到的数据 */ pdata++; } SPI_FLASH_CS_HIGH(); /* 停止通讯: CS 高电平 */ }
/** * @brief 写入数据 * @param pdata:写入数据缓存 addr:写入起始地址 numByteToWrite:写入数据数量 * @retval 无 */ void SPI_Flash_WriteData(u8* pdata,u32 addr,u32 numByteToWrite) { WateForReady(); /* 等待Flash内部时序完成,主要是读芯片的状态字 */ SPI_FLASH_Write_Enable(); /* 开始写入前先写使能 */ SPI_FLASH_CS_LOW(); /* 开始通讯: CS 低电平 */ SPI_FLASH_SendByte(PageProgram); /* 下发写指令(页) */ SPI_FLASH_SendByte((addr & 0xFF0000) >> 16 ); /* 取16-23位 */ SPI_FLASH_SendByte((addr & 0xFF00) >> 8); /* 取8-15位 */ SPI_FLASH_SendByte(addr & 0xFF); /* 取0-7位 */ while(numByteToWrite--) /* 循环写入数据 */ { SPI_FLASH_SendByte(*pdata); /* 下发写入数据 */ pdata++; } SPI_FLASH_CS_HIGH(); /* 停止通讯: CS 高电平 */ }
#include "stm32f4xx.h" #include "bsp_led.h" #include "bsp_systick.h" #include "bsp_usart_dma.h" #include "bsp_spi_flash.h" #include <stdio.h> u8 ReadBuffer[4096] = {0x00}; //读取数据缓冲区 u8 WriteBuffer[256] = {0x00}; //写入数据缓冲区 int main(void) { u32 device_id = 0; u32 i = 0; LED_Config(); DEBUG_USART1_Config(); SysTick_Init(); SPI_GPIO_Config(); printf("\r\n这是SPI读取FLASH_Device_ID的测试实验!\r\n"); SPI_Flash_WakeUp(); /* *********************** 读取Flash ID ************************** */ device_id = SPI_FLASH_ReadID(); printf("\r\ndevice_id = 0x%X\r\n",device_id); Delay_ms(1000); /* *********************** 擦除扇区 ************************** */ SPI_Flash_Erase(0x00); /* 擦除扇区 */ SPI_Flash_ReadDate(ReadBuffer,0x00,4096); /* 擦除扇区后读取扇区内的数据,擦除动作是将扇区内寄存器全部置1 */ printf("\r\n**************读出擦除后的数据****************\r\n"); for(i = 0;i<4096;i++) { printf("0x%02x ",ReadBuffer[i]); /* 若擦除成功,则读取到的数据应该全部为oxFF */ } for(i = 0;i<256;i++) /* 向写入缓冲区数据写入数据 */ { WriteBuffer[i] = i; } SPI_Flash_WriteData(WriteBuffer,0x00,256); /* 这里执行的是PageProgram指令,为页写入,一次只能写入256个数据 */ SPI_Flash_ReadDate(ReadBuffer,0x00,256); /* 写入完成后,再读取出来 */ printf("\r\n**************读出写入后的数据****************\r\n"); for(i = 0;i<256;i++) { printf("0x%02x ",ReadBuffer[i]); /* 若写入成功,这里读取到的数据应该为0x00 ~ 0xFF */ } SPI_Flash_PowerDown(); /* 操作完成后,发送掉电指令 */ while(1) { } }
在这里为了测试方便,我将第一次读取的数据亮也改成256个,方便截图,效果如下:
这里需要注意的是,在写入数据的时候,我们用的是PageProgram指令,该指令为页写入,每次最多只能写入1页数据,且数据最多为256个,而且这里写入是只能在单页写入,不能跨页写入,我测试过,起始地址改为0x20,写入256个数据,按道理最后一个写入地址应该是0x1FF,但是写入后再读取数据不对,后来查了一下,这里遇到的问题和I2C读写EEPROM的是一样的,我大概总结了一下,对Flash的数据写入分为以下几种:
1、写入首地址与页首地址相同:
a、写入数据 ≤ 256 byte;
b、写入数据 = (n * 256) + m (n为页数,m为不满1页数据量,m < 256)
2、写入首地址与页首地址不同:
a、写入数据 ≤ 256 byte (一页可以写完);
b、写入数据 = x+ (n * 256) + m(x为前端不满一页的数据量,x = 0时,表示字节对齐,n为页数,m为不满1页数据量,m < 256)
所以综上所述,写入数据量最终公式应该为:
W
r
i
t
e
B
u
f
f
e
r
=
x
+
(
n
∗
256
)
+
m
WriteBuffer = x+ (n * 256) + m
WriteBuffer=x+(n∗256)+m
因此这里将上面的void SPI_Flash_WriteData(u8* pdata,u32 addr,u32 numByteToWrite)
在完善以下,写一个进阶版的void SPI_Flash_WriteBuffer(u8* pdata,u32 addr,u32 numByteToWrite)
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { /* NumOfPage: 计算需要写入的页数; * NumOfSingle: 计算出不满一页时剩余的数据量 * Addr: 写入地址与SPI_FLASH_PageSize求余,为0则与页首对齐; * count: 计算前端差多少数据可以与页首对齐; */ u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; Addr = WriteAddr % SPI_FLASH_PageSize; count = SPI_FLASH_PageSize - Addr; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; /* 情况1:页首对齐 */ if (Addr == 0) { /* 情况1.a :写入首地址与页首地址相同,写入数据 ≤ 256 byte */ if (NumOfPage == 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } else /* 情况1.b :写入首地址与页首地址相同 ,写入数据超过1页 */ { while (NumOfPage--) /* 将整页数据逐页写完 */ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } if(NumOfSingle !=0 ) /* 若尾端仍有数据,将剩余数据写完 */ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } else /* 情况2:页首不对齐 */ { if (NumOfPage == 0) /* 情况2.a :页首不对齐,写入数据 ≤ 256 byte */ { /* 数据不超过256个,但是跨页,情况可在细分 */ if (NumOfSingle > count) /* 数据不超过256,但当首地址当页不能写完 */ { temp = NumOfSingle - count; SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); /* 先将首地址页数据写完 */ WriteAddr += count; pBuffer += count; SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp); /* 下一页数据在写入 */ } else /*数据不超过256个,且首地址当页能将所有数据写完 */ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } } else /* 情况2.b 首地址不对齐,且数据量超256个 */ { NumByteToWrite -= count; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; while (NumOfPage--) /* 先写整页 */ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } if (NumOfSingle != 0) /* 再写多出来的数据 */ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } } }
这里的写入数据实现和I2C向EEPROM写入数据基本是一致的,不懂得可以看一下I2C的内容。
最后,将在main函数中调用的测试程序贴出来:
SPI_Flash_Erase(0x20); SPI_Flash_ReadDate(ReadBuffer,0x20,4096); printf("\r\n**************读出擦除后的数据****************\r\n"); for(i = 0;i<4096;i++) { printf("0x%02x ",ReadBuffer[i]); } for(i = 0;i<4096;i++) { WriteBuffer[i] = i; } SPI_FLASH_BufferWrite(WriteBuffer,0x20,4096); SPI_Flash_ReadDate(ReadBuffer,0x20,4096); printf("\r\n**************读出写入后的数据****************\r\n"); for(i = 0;i<4096;i++) { printf("0x%02x ",ReadBuffer[i]); }
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。