W25Q128的读写采用的是SPI接口;本人移植的程序是在STM32上验证过的,只需要修改相应的底层函数即可;
初始化SPI接口;
void MX_SPI_Flash_Init(void)
{
SPI_InitStructure SPI_initStruct;
PORT_Init(PORTA, PIN5, PORTA_PIN5_SPI0_SSEL, 0);
PORT_Init(PORTA, PIN6, PORTA_PIN6_SPI0_MISO, 1);
PORT_Init(PORTA, PIN7, PORTA_PIN7_SPI0_MOSI, 0);
PORT_Init(PORTA, PIN8, PORTA_PIN8_SPI0_SCLK, 0);
SPI_initStruct.FrameFormat = SPI_FORMAT_SPI;//帧格式:SPI_FORMAT_SPI ; SPI_FORMAT_TI_SSI
SPI_initStruct.SampleEdge = SPI_FIRST_EDGE;在SPI帧格式下,选择数据采样边沿:1 SPI_FIRST_EDGE 、2 SPI_SECOND_EDGE
SPI_initStruct.IdleLevel = SPI_LOW_LEVEL;//在SPI帧格式下,选择空闲时(无数据传输时)时钟线的电平: SPI_LOW_LEVEL 、 SPI_HIGH_LEVEL
SPI_initStruct.WordSize = 8;//字长度, 有效值4-16
SPI_initStruct.Master = 1;//1 主机模式 0 从机模式
SPI_initStruct.clkDiv = SPI_CLKDIV_256;// SPI_CLK = SYS_CLK / clkDiv,有效值:SPI_CLKDIV_4、SPI_CLKDIV_8、... ... 、SPI_CLKDIV_512
SPI_initStruct.RXThresholdIEn = 0;//当RX FIFO中数据个数 >= RXThreshold时触发中断
SPI_initStruct.TXThresholdIEn = 0;//当TX FIFO中数据个数 <= TXThreshold时触发中断
SPI_initStruct.TXCompleteIEn = 0;//发送FIFO空且发送移位寄存器空中断使能
SPI_FLASH_CS_H();
SPI_Init(SPI0, &SPI_initStruct);
SPI_Open(SPI0);
SPI2_WriteByte( 0xFF);
}
修改底层函数,主要是SPI的读写函数,参照官方SWM181读写flash的写法,全部采用SPI_ReadWrite();来进行SPI通信的读写操作;
uint8_t SPI2_ReadByte(void)
{
return SPI_ReadWrite(SPI0, 0xFF);
}
void SPI2_WriteByte(uint8_t TxData)
{
SPI_ReadWrite(SPI0, TxData);
}
/*SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节*/
uint8_t SPI2_ReadWriteByte(uint8_t TxData)
{
return SPI_ReadWrite(SPI0, TxData);
}
注意踩坑部分!!!!!!
因为是项目需要,我的程序中有一个串口是一直在接收蓝牙扫描的广播信息的,该串口我采用的是DMA的方式进行接收的,一直会有DMA中断过来影响到SPI接口读写W25Q,也就是说,在SPI读写W25Q时不能有任何中断来打断它,故最后我修改代码部分如下:
#if 0
#define SPI_FLASH_CS_L() GPIO_ClrBit(GPIOA, PIN5)
#define SPI_FLASH_CS_H() GPIO_SetBit(GPIOA, PIN5) //GPIO_SetBit(GPIOA, PIN5)
#else
void SPI_FLASH_CS_L(void)
{
UART_Close(UART0);
GPIO_ClrBit(GPIOA, PIN5); // SPI_CS_Low()
}
void SPI_FLASH_CS_H(void)
{
GPIO_SetBit(GPIOA, PIN5); // SPI_CS_Low()
UART_Open(UART0);
}
#endif
在SPI通信时关闭影响的串口,读写测试,全部OK
同样的用法,这种问题在STM32上并没有出现,国产芯片任重而道远呐
附W25Q读写部分,主要是看底层关于SPI的操作,关于W25Q的读写是从网上找的前辈的程序修改的
"w25q128.h"
#ifndef __W25Q128_H__
#define __W25Q128_H__
#include "SWM260.h"
#include "SWM260_spi.h"
//SPIflash
#define FLASH_ID 0XEF17 //W25Q128
//指令表
#define W25X_WriteEnable 0x06 //写使能
#define W25X_WriteDisable 0x04 //写失能
#define W25X_ReadStatusReg 0x05 //读控制和状态寄存器
#define W25X_WriteStatusReg 0x01 //写控制和状态寄存器
#define W25X_ReadData 0x03 //读数据
#define W25X_FastReadData 0x0B //快速读数据
#define W25X_FastReadDual 0x3B //快速读取双通道输出
#define W25X_PageProgram 0x02 //页写
#define W25X_BlockErase 0xD8 //64KB块擦除
#define W25X_SectorErase 0x20 //扇区擦除4k
#define W25X_ChipErase 0xC7 //整片擦除
#define W25X_PowerDown 0xB9 //待机模式
#define W25X_ReleasePowerDown 0xAB //读芯片ID
#define W25X_DeviceID 0xAB //读芯片ID
#define W25X_ManufactDeviceID 0x90 //读芯片ID 读取制造商/设备ID
#define W25X_JedecDeviceID 0x9F //读 JEDEC ID (9Fh)
//片选使能位
//spi FLASH w25Q128 flash读写操作函数
void MX_SPI_Flash_Init(void);//初始化
uint8_t SPI2_ReadByte(void); //返回通过SPIx最近接收的数据
void SPI2_WriteByte(uint8_t TxData); //通过外设SPIx发送一个数据
uint8_t SPI2_ReadWriteByte(uint8_t TxData);//发送并接收返回的一个数据
void SPI_FLASH_Write_Enable(void); //SPI_FLASH写使能 //将WEL置位
void SPI_FLASH_Write_Disable(void); //SPI_FLASH写禁止 //将WEL清零
uint8_t SPI_Flash_ReadSR(void); //读取SPI_FLASH的状态寄存器
void SPI_Flash_Wait_Busy(void); //等待空闲
uint16_t SPI_Flash_ReadID(void); //读取芯片ID W25Q128的ID:0XEF17
void SPI_Flash_Erase_Chip(void) ; //擦除整个芯片 W25Q128 80S
void SPI_Flash_Erase_Sector(uint32_t Dst_Addr);//擦除一个扇区
void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer) ; //读取SPI FLASH
void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);//SPI在一页(0~65535)内写入少于256个字节的数据
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//无需校验写入
#endif
"w25q128.c"
#include "w25q128.h"
#define SpiW25Q SPI0
#if 0
#define SPI_FLASH_CS_L() GPIO_ClrBit(GPIOA, PIN5)
#define SPI_FLASH_CS_H() GPIO_SetBit(GPIOA, PIN5) //GPIO_SetBit(GPIOA, PIN5)
#else
void SPI_FLASH_CS_L(void)
{
UART_Close(UART0);
GPIO_ClrBit(GPIOA, PIN5); // SPI_CS_Low()
}
void SPI_FLASH_CS_H(void)
{
GPIO_SetBit(GPIOA, PIN5); // SPI_CS_Low()
UART_Open(UART0);
}
#endif
/*-------------------------SPIflash----------------------------------------------------W25Q128---------------------------------------------*/
void MX_SPI_Flash_Init(void)
{
SPI_InitStructure SPI_initStruct;
PORT_Init(PORTA, PIN5, PORTA_PIN5_SPI0_SSEL, 0);
PORT_Init(PORTA, PIN6, PORTA_PIN6_SPI0_MISO, 1);
PORT_Init(PORTA, PIN7, PORTA_PIN7_SPI0_MOSI, 0);
PORT_Init(PORTA, PIN8, PORTA_PIN8_SPI0_SCLK, 0);
SPI_initStruct.FrameFormat = SPI_FORMAT_SPI;//帧格式:SPI_FORMAT_SPI ; SPI_FORMAT_TI_SSI
SPI_initStruct.SampleEdge = SPI_FIRST_EDGE;在SPI帧格式下,选择数据采样边沿:1 SPI_FIRST_EDGE 、2 SPI_SECOND_EDGE
SPI_initStruct.IdleLevel = SPI_LOW_LEVEL;//在SPI帧格式下,选择空闲时(无数据传输时)时钟线的电平: SPI_LOW_LEVEL 、 SPI_HIGH_LEVEL
SPI_initStruct.WordSize = 8;//字长度, 有效值4-16
SPI_initStruct.Master = 1;//1 主机模式 0 从机模式
SPI_initStruct.clkDiv = SPI_CLKDIV_256;// SPI_CLK = SYS_CLK / clkDiv,有效值:SPI_CLKDIV_4、SPI_CLKDIV_8、... ... 、SPI_CLKDIV_512
SPI_initStruct.RXThresholdIEn = 0;//当RX FIFO中数据个数 >= RXThreshold时触发中断
SPI_initStruct.TXThresholdIEn = 0;//当TX FIFO中数据个数 <= TXThreshold时触发中断
SPI_initStruct.TXCompleteIEn = 0;//发送FIFO空且发送移位寄存器空中断使能
SPI_FLASH_CS_H();
SPI_Init(SPI0, &SPI_initStruct);
SPI_Open(SPI0);
SPI2_WriteByte( 0xFF);
}
uint8_t SPI2_ReadByte(void)
{
return SPI_ReadWrite(SPI0, 0xFF);
}
void SPI2_WriteByte(uint8_t TxData)
{
SPI_ReadWrite(SPI0, TxData);
}
/*SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节*/
uint8_t SPI2_ReadWriteByte(uint8_t TxData)
{
return SPI_ReadWrite(SPI0, TxData);
}
/*SPI_FLASH写使能 将WEL置位 */
void SPI_FLASH_Write_Enable(void)
{
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_WriteEnable); //发送``写使能 0x06
SPI_FLASH_CS_H(); //取消片选
}
/*SPI_FLASH写禁止 将WEL清零 */
void SPI_FLASH_Write_Disable(void)
{
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_WriteDisable); //发送写禁止指令 ```0x04
SPI_FLASH_CS_H(); //取消片选
}
/*读取SPI_FLASH的状态寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
*/
uint8_t SPI_Flash_ReadSR(void)
{
uint8_t byte=0;
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 ```0x05
byte = SPI2_ReadByte(); //读取一个字节
SPI_FLASH_CS_H(); //取消片选
return byte;
}
/*等待空闲*/
void SPI_Flash_Wait_Busy(void)
{
while ((SPI_Flash_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
/*读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
*/
uint16_t SPI_Flash_ReadID(void)
{
uint16_t Temp = 0;
SPI_FLASH_CS_L();
SPI2_WriteByte(0x90);//发送读取ID命令 0x90
SPI2_WriteByte(0x00);
SPI2_WriteByte(0x00);
SPI2_WriteByte(0x00);
Temp|=SPI2_ReadByte()<<8;
Temp|=SPI2_ReadByte();
SPI_FLASH_CS_H();
return Temp;
}
/*擦除整个芯片
//整片擦除时间:
//W25X16:25s
//W25X32:40s
//W25X64:40s
//等待时间超长...
*/
void SPI_Flash_Erase_Chip(void)
{
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_ChipErase); //发送片擦除命令
SPI_FLASH_CS_H(); //取消片选
SPI_Flash_Wait_Busy(); //等待芯片擦除结束
}
/*擦除一个扇区;Dst_Addr:扇区地址 根据实际容量设置;擦除一个扇区的最少时间:150ms 4k */
void SPI_Flash_Erase_Sector(uint32_t Dst_Addr)
{
Dst_Addr*=4096;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_SectorErase); //发送扇区擦除命令
SPI2_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); //发送24bit地址
SPI2_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
SPI2_ReadWriteByte((uint8_t)Dst_Addr);
SPI_FLASH_CS_H(); //取消片选
SPI_Flash_Wait_Busy(); //等待芯片擦除结束
}
/*读取SPI FLASH
在指定地址开始读取指定长度的数据
ReadAddr:开始读取的地址(24bit)
NumByteToRead:要读取的字节数(最大65535)
pBuffer:数据存储区
*/
void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer)
{
uint16_t i;
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_ReadData); //发送读取命令
SPI2_WriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
SPI2_WriteByte((uint8_t)((ReadAddr)>>8));
SPI2_WriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer = SPI2_ReadByte(); //循环读数
}
SPI_FLASH_CS_H();//取消片选
}
/*SPI在一页(0~65535)内写入少于256个字节的数据
在指定地址开始写入最大256字节的数据
pBuffer:数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
*/
void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)
{
uint16_t i;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_FLASH_CS_L(); //使能器件
SPI2_WriteByte(W25X_PageProgram); //发送写页命令
SPI2_WriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
SPI2_WriteByte((uint8_t)((WriteAddr)>>8));
SPI2_WriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++) SPI2_WriteByte(pBuffer);//循环写数
SPI_FLASH_CS_H(); //取消片选
SPI_Flash_Wait_Busy(); //等待写入结束
}
/*
无检验写SPI FLASH ;必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
具有自动换页功能 ;在指定地址开始写入指定长度的数据,但是要确保地址不越界!
pBuffer:数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
CHECK OK
*/
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; //单页剩余的字节数
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
while(1)
{
SPI_Flash_Write_Page(WriteAddr,pageremain,pBuffer);
if(NumByteToWrite==pageremain)
break;//写入结束了
else //NumByteToWrite>pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; //减去已经写入了的字节数
if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
else pageremain=NumByteToWrite; //不够256个字节了
}
}
}
————————————————
版权声明:本文为CSDN博主「QAQlfk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40307946/article/details/111033977
|