文章目录
一.概要
FLASH是一种存储芯片,通过程序可以修改数据,即平时所说的“闪存”。
FLASH闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何Flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。
SPI Flash使用SPI接口进行数据传输,采用一种主从模式。主控制器通过发送命令和地址来访问SPI Flash,然后接收或写入数据。SPI Flash在接收到命令后,将相应的数据返回给主控制器。
SPI Flash的读取速度相对较慢,不支持直接执行代码。因此,它更适合用于存储配置数据、固件升级或数据存储等需要大容量存储但不需要频繁读取的应用。
本文对W25Q32 SPI FLASH的原理,指令,通讯时序等进行讲解,并通过STM32F103C8T6单片机对W25Q32 进行数据读写实验。
二.W25Q32 SPI FLASH主要参数
FLASH芯片型号:W25Q32JVSSIQ
制造商:Winbond
产品种类:SPI NOR FLASH
封装 :SOIC-8
系列:W25Q32JV
存储容量:32 Mbit
最大时钟频率:133 MHz
接口类型:SPI
数据总线宽度:8 bit
电源电压: 2.7~3.6 V
电源电流—最大值: 25 mA
最小工作温度: - 40°C~85°C
模块接口说明:
1.VCC 供电电源 3.3V
2.CS SPI通讯CS引脚
3.DO SPI通讯MISO引脚
4.GND 电源地
5.CLK SPI通讯CLK引脚
6.DI SPI通讯MOSI引脚
三.W25Q32 SPI FLASH芯片介绍
引脚定义
1.W25Q32 芯片内部框图
如上图所示:
W25Q32的存储单位
Page(页)
256字节,编程最小单位(向FLASH写入内容),一次最多编程256字节。
Sector(扇区)
擦除的最小单位,1个Sector一般包含16个Page,即4KB。
Block(块)
包含16个Sector,块擦除可以32KB(半块)、64KB(整块)两种擦除方式。
2.W25Q32 芯片指令表格
下图是指令表格,每个不同的指令所发的字节数量是不同的,第一个字节发送的都是命令号,比如擦除指令,擦除第0个Sector,SPI所发送的内容是0x20,0x00,0x00,0x00这四个字节。
3.W25Q32 芯片通讯时序
我们以读数据为例,解析下通讯时序,根据SPI通讯规则,CPOL为0,CPHA为0,MCU提供CLK,跟CS控制。
根据指令表格,读的命令4字节内容,是0x03+3字节地址,就能获取到对应地址的数据内容,数据内容是1字节。
四.W25Q32 SPI FLASH读写实验
硬件准备:
STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口。
用6根杜邦线把模块与开发板相连
板子3.3----模块VCC
板子A4-----模块CS
板子A5-----模块CLK
板子A6-----模块DO
板子A7-----模块DI
板子G------模块GND
打开STM32CubeMX软件,新建工程
Part Number处输入STM32F103C8,再双击就创建新的工程
配置下载口引脚
配置外部晶振引脚
配置系统主频
SPI1引脚配置,以及PA4引脚普通GPIO配置
配置工程文件名,保存路径,KEIL5工程输出方式
生成工程
用Keil5打开工程
添加相关代码
主要代码
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
FlashJedecid = spi_flash_read_id();//读取Jedecid
FlashDeviceid=SFLASH_ReadID();//读取Device ID
Flash_ReadSomeBytes(ReadBuff,0,8);//从FLASH 0地址读取8字节内容放入ReadBuff数组
Flash_WriteSR(0x42);//解除保护
HAL_Delay(100);//等待100ms
Flash_ReadSR();//读状态寄存器
Flash_WriteSomeBytes(WriteBuff,0,8);//把WriteBuff数组中的内容写入FLASH 0地址
HAL_Delay(100);//等待100ms
Flash_ReadSomeBytes(ReadBuff,0,8);//从FLASH 0地址读取8字节内容放入ReadBuff数组
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
void Flash_ErasePage(uint32_t _ulPageAddr)
{
_ulPageAddr *= 256;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_0();
spi_master_send_recv_byte(FLASH_ERASE_PAGE);//页擦除指令
spi_master_send_recv_byte((uint8_t)(_ulPageAddr>>16)); //写入24位地址
spi_master_send_recv_byte((uint8_t)(_ulPageAddr>>8));
spi_master_send_recv_byte((uint8_t)(_ulPageAddr>>0));
FLASH_CS_1();
Flash_WaitNobusy(); //等待写入结束
}
void Flash_EraseSector(uint32_t _ulSectorAddr)
{
uint8_t command = FLASH_ERASE_SECTOR;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulSectorAddr >> 16);
temp_buff[1] = (uint8_t)(_ulSectorAddr >> 8);
temp_buff[2] = (uint8_t)(_ulSectorAddr >> 0);
_ulSectorAddr *= 4096; //1个扇区 4 KBytes
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_0();
spi_master_send_recv_byte(command);
spi_master_send_recv_byte(temp_buff[0]);
spi_master_send_recv_byte(temp_buff[1]);
spi_master_send_recv_byte(temp_buff[2]);
FLASH_CS_1();
Flash_WaitNobusy(); //等待写入结束
}
void Flash_EraseBlock(uint32_t _ulBlockAddr)
{
uint8_t command = FLASH_ERASE_BLOCK;
_ulBlockAddr *= 65536; //块地址,一块64K
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_0();
spi_master_send_recv_byte(command);
spi_master_send_recv_byte(_ulBlockAddr>>16);
spi_master_send_recv_byte(_ulBlockAddr>>8);
spi_master_send_recv_byte(_ulBlockAddr>>0);
FLASH_CS_1();
Flash_WaitNobusy(); //等待写入结束
}
void Flash_EraseChip(void)
{
uint8_t command = FLASH_ERASE_CHIP;
Flash_WriteEnable(); //flash芯片写使能
Flash_WaitNobusy(); //等待写操作完成
FLASH_CS_0();
spi_master_send_recv_byte(command);
FLASH_CS_1();
Flash_WaitNobusy(); //等待写入结束
}
void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_READ_DATA;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);
temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);
temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);
FLASH_CS_0();
spi_master_send_recv_byte(command);
spi_master_send_recv_byte(temp_buff[0]);
spi_master_send_recv_byte(temp_buff[1]);
spi_master_send_recv_byte(temp_buff[2]);
spi_master_recv_some_bytes(ucpBuffer, _usNByte);
FLASH_CS_1();
}
五.CubeMX工程源代码下载
链接:https://pan.baidu.com/s/1UErozpM7nE7LMovXO3FILw
提取码:15di
如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行
六.小结
SPI Flash广泛应用于各种产品中,如智能手机、平板电脑、路由器、网络设备、工业控制系统、汽车电子、穿戴设备等。由于其灵活性和可靠性,SPI Flash成为许多嵌入式系统首选的存储解决方案之一。