2.STM32 Cube IDE配置
配置好之后生成代码就行了,然后再自己编辑一个驱动测试的c文件。
三、指令解析
1、用到的相关指令
| 指令 | 名称 | 解释 |
| 02h | Page Program | 页编程,在一页上写字 |
| 03h | Read Data | 读取数据 |
| 05h | Read Status Register | 读取寄存器状态 |
| 06h | Write Enable | 将状态寄存器中的写启用闩锁(WEL)位设置为1。 |
| 20h | Sector Erase | 扇区擦除 |
| C7h/60h | Chip Erase | 整个芯片擦除 |
2、读取设备ID(举一个例子,其他指令对照芯片手册看即可)
读取设备ID指令根据数据手册,发送0X90+24位地址之后,就可以接收到0XEF + ID,W25Q128的ID为0X17。read_W25Q128_ID()函数可以通过串口打印ID,或者通过单步调试Debug直接查看读到的ID值。
可以理解为MCU向W25Q128发送命令0x90 0x00 0x00 0x00 ,然后就可以接收到W25Q128的两个字节 0xEF 0x17
// 读取 ID 测试 OK 0xEF 0X17
void read_W25Q128_ID()
{
uint8_t _RxData[2]={0x00};
W25Q128_Enable();
//发送指令
spi2_Transmit_one_byte(0x90);
spi2_Transmit_one_byte(0x00);
spi2_Transmit_one_byte(0x00);
spi2_Transmit_one_byte(0x00);
//接收数据
_RxData[0] = spi2_Receive_one_byte();
_RxData[1] = spi2_Receive_one_byte();
W25Q128_Disable();
printf("%s\r\n",_RxData); //串口打印 ID
}
2、读、写、擦除操作
读、写、擦除操作的24位地址取值范围是0-16777216,因为读可以从指定地址一直读到最后,而写,一次最多写一页,擦除的最小单位为一个扇区4096个字即16页,当然也可以一不做二不休整个芯片擦除,这个擦除时间比较长十几秒,因为是自学,所以总得做点什么。比如:
1、写10个数,卡在第一页和第二页之间,即第一页写5个数第二页写5个数。
2、写10个数,卡在第一个扇区和第二个扇区之间,即第255页写5个数第256页写5个 数。
**问题点:**第一个问题,就要考虑翻页写的问题,第二个问题就要考虑擦除两个扇区和翻页写的问题。
解决思路:
1、通过地址定位到当页还剩下多少个字可以写,通过要写字的个数,分为几次写,写完当页后再翻页写到下一页,直到写完。相关函数:
Write_Page() Write_Word()
2、因为要先进行擦除,然后再写数据,才能保证写入数据的准确性,可以直接擦除整个芯片,要想时间最快,擦除部分应该是最小的,所以选择擦除扇区。通过地址和要写字的个数,就可以判断,要写的地方在哪几个扇区,然后执行擦除即可。相关函数:
Erase_Write_data_Sector() Erase_one_Sector()
四、驱动代码
W25Q28.c文件
#include "W25Q128.h"
#include "spi.h"
#include "usart.h"
#include <stdio.h>
uint32_t FLASH_SIZE=16*1024*1024; //FLASH 大小为16M字节
uint32_t Data_Address = 4090; //测试地址 250(地址在两页之间) 和 4090 (地址在两扇区并且两页之间)
//要写的数据
uint8_t Write_data[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x41};
#define Write_data_SIZE sizeof(Write_data)
//要读的数据
uint8_t Read_data[100] = {0};
#define Read_data_SIZE sizeof(Read_data)
/* Nicky ******************************************************************* */
//器件使能
void W25Q128_Enable()
{
HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, RESET); // Chip select
}
/* Nicky ******************************************************************* */
//器件失能
void W25Q128_Disable()
{
HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, SET); // Chip disselect
}
/* Nicky ******************************************************************* */
//SPI2 发送 1 个字节数据
void spi2_Transmit_one_byte(uint8_t _dataTx)
{
HAL_SPI_Transmit(&hspi2,(uint8_t*) &_dataTx,1,HAL_MAX_DELAY);
}
/* Nicky ******************************************************************* */
//SPI2 接收 1 个字节数据
uint8_t spi2_Receive_one_byte()
{
uint16_t _dataRx;
HAL_SPI_Receive(&hspi2,(uint8_t*) &_dataRx, 1, HAL_MAX_DELAY);
return _dataRx;
}
/* Nicky ******************************************************************* */
//W25Q128写使能,将WEL置1
void W25Q128_Write_Enable()
{
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x06);
W25Q128_Disable(); //取消片选
}
/* Nicky ******************************************************************* */
//W25Q128写失能,将WEL置0
void W25Q128_Write_Disable()
{
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x04);
W25Q128_Disable(); //取消片选
}
/* Nicky ******************************************************************* */
//读取寄存器状态
uint8_t W25Q128_ReadSR(void)
{
uint8_t byte=0;
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x05); //发送读取状态寄存器命令
byte=spi2_Receive_one_byte(); //读取一个字节
W25Q128_Disable(); //取消片选
return byte;
}
/* Nicky ******************************************************************* */
//等待空闲
void W25Q128_Wait_Busy()
{
while((W25Q128_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
/* Nicky ******************************************************************* */
//擦除地址所在的一个扇区
void Erase_one_Sector(uint32_t Address)
{
W25Q128_Write_Enable(); //SET WEL
W25Q128_Wait_Busy();
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x20); //发送扇区擦除指令
spi2_Transmit_one_byte((uint8_t)((Address)>>16)); //发送24bit地址
spi2_Transmit_one_byte((uint8_t)((Address)>>8));
spi2_Transmit_one_byte((uint8_t)Address);
W25Q128_Disable(); //取消片选
W25Q128_Wait_Busy(); //等待擦除完成
}
/* Nicky ******************************************************************* */
//擦除地址所在的扇区
void Erase_Write_data_Sector(uint32_t Address,uint32_t Write_data_NUM)
{
//总共4096个扇区
//计算 写入数据开始的地址 + 要写入数据个数的最后地址 所处的扇区
uint16_t Star_Sector,End_Sector,Num_Sector;
Star_Sector = Address / 4096; //数据写入开始的扇区
End_Sector = (Address + Write_data_NUM) / 4096; //数据写入结束的扇区
Num_Sector = End_Sector - Star_Sector; //数据写入跨几个扇区
//开始擦除扇区
for(uint16_t i=0;i <= Num_Sector;i++)
{
Erase_one_Sector(Address);
Address += 4095;
}
}
/* Nicky ******************************************************************* */
//擦除整个芯片 等待时间超长... 10-20S
void Erase_W25Q128_Chip(void)
{
W25Q128_Write_Enable(); //SET WEL
W25Q128_Wait_Busy();
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x60); //发送片擦除命令
W25Q128_Disable(); //取消片选
W25Q128_Wait_Busy(); //等待芯片擦除结束
}
/* Nicky ******************************************************************* */
//读取W25Q128数据
void Read_W25Q128_data(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i=0;
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x03); //发送读取命令
spi2_Transmit_one_byte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
spi2_Transmit_one_byte((uint8_t)((ReadAddr)>>8));
spi2_Transmit_one_byte((uint8_t)ReadAddr);
for(;i<NumByteToRead;i++)
{
pBuffer[i]=spi2_Receive_one_byte(); //循环读数
}
W25Q128_Disable();
}
/* Nicky ******************************************************************* */
//写字,一次最多一页
void Write_Word(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t i;
W25Q128_Write_Enable(); //SET WEL
W25Q128_Enable(); //使能器件
spi2_Transmit_one_byte(0x02);
spi2_Transmit_one_byte((uint8_t)((WriteAddr) >> 16)); //写入的目标地址
spi2_Transmit_one_byte((uint8_t)((WriteAddr) >> 8));
spi2_Transmit_one_byte((uint8_t)WriteAddr);
for (i = 0; i < NumByteToWrite; i++)
spi2_Transmit_one_byte(pBuffer[i]);//循环写入字节数据
W25Q128_Disable();
W25Q128_Wait_Busy(); //写完之后需要等待芯片操作完。
}
/* Nicky ******************************************************************* */
//定位到页
void Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t Word_remain;
Word_remain=256-WriteAddr%256; //定位页剩余的字数
if(NumByteToWrite <= Word_remain)
Word_remain=NumByteToWrite; //定位页能一次写完
while(1)
{


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**