0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅谈STM32之SPI_FLASH之应用实例

ss 来源:未知 作者:沈丹 2018-10-07 11:29 次阅读
SPI Flash
首先它是个Flash,Flash是什么东西就不多说了(非易失性存储介质),分为NOR和NAND两种(NOR和NAND的区别本篇不做介绍)。SPI一种通信接口。那么严格的来说SPI Flash是一种使用SPI通信的Flash,即,可能指NOR也可能是NAND。但现在大部分情况默认下人们说的SPI Flash指的是SPI NorFlash。早期Norflash的接口是parallel的形式,即把数据线和地址线并排与IC的管脚连接。但是后来发现不同容量的Norflash不能硬件上兼容(数据线和地址线的数量不一样),并且封装比较大,占用了较大的PCB板位置,所以后来逐渐被SPI(串行接口)Norflash所取代。同时不同容量的SPI Norflash管脚也兼容封装也更小。,至于现在很多人说起NOR flash直接都以SPI flash来代称。
NorFlash根据数据传输的位数可以分为并行(Parallel,即地址线和数据线直接和处理器相连)NorFlash和串行(SPI,即通过SPI接口和处理器相连)NorFlash;区别主要就是:1、SPI NorFlash每次传输一bit位的数据,parallel连接的NorFlash每次传输多个bit位的数据(有x8和x16bit两种); 2、SPI NorFlash比parallel便宜,接口简单点,但速度慢。
NandFlash是地址数据线复用的方式,接口标准统一(x8bit和x16bit),所以不同容量再兼容性上基本没什么问题。但是目前对产品的需求越来越小型化以及成本要求也越来越高,所以SPI NandFlash渐渐成为主流,并且采用SPI NANDFlash方案,主控也可以不需要传统NAND控制器,只需要有SPI接口接口操作访问,从而降低成本。另外SPI NandFlash封装比传统的封装也小很多,故节省了PCB板的空间。
怎么用说白了对于Flash就是读写擦,也就是实现flash的驱动。先简单了解下spi flash的物理连接。
之前介绍SPI的时候说过,SPI接口目前的使用是多种方式(具体指的是物理连线有几种方式),Dual SPI、Qual SPI和标准的SPI接口(这种方式肯定不会出现在连接外设是SPI Flash上,这玩意没必要全双工),对于SPI Flash来说,主要就是Dual和Qual这两种方式。具体项目具体看了,理论上在CLK一定的情况下, 线数越多访问速度也越快。我们项目采用的Dual SPI方式,即两线。

本实例用的是STM32F103VET6平台,它有3个SPI接口(这里使用SPI1),各信号线连接到FLASH(型号:W25X16)的CS,CLK,DO,DIO线,以实现SPI通讯,对FLASH进行读写。

(这里采用主模式,全双工通讯,通过查询发送数据寄存器和接收数据寄存器状态确保通讯正常)

mian函数:

1#define sFLASH_ID 0xEF3015(前面加个1,免得变大)

u32 DeviceID;

u32 FlashID;

int main(void)

{

/115200 8-N-1/

USART1_Config();

SPI_FLASH_Init();

DeviceID = SPI_FLASH_ReadDeviceID();

Delay(200);

FlashID = SPI_FLASH_ReadID();

printf(“\r\n FlashID is 0x%X, Manufacturer Device ID is 0x%X\r\n”,FlashID,DeviceID);

if(FlashID == sFLASH_ID)

{

printf(“\r\n 检测到 flash W25X16 !\r\n”);

SPI_FLASH_SectorErase(FLASH_SectorToErase);

SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);

printf(“\r\n 写入的数据为:%s \r\t”, Tx_Buffer);

SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);

printf(“\r\n 读出的数据为:%s \r\n”, Tx_Buffer);

TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);

if( PASSED == TransferStatus1)

{

printf(“\r\n 2M 串行 flash(W25X16)测试成功!\n\r”);

}

else

{

printf(“\r\n 2M 串行 flash(W25X16)测试失败!\n\r”);

}

}

else

{

printf(“\r\n 获取不到 W25X16 ID!\n\r”);

}

SPI_Flash_PowerDown();

while(1);

1234567891011121314151617181920212223242526272829303132333435363738

}

mian函数的流程:

1,调用 USART1_Config() 初始化串口;

2,调用 SPI_FLASH_Init() 初始化SPI模块;

3,调用 SPI_FLASH_ReadDeviceID 读取FLASH器件生产厂商的ID信息;

4,调用 SPI_FLASH_ReadID 读取FLASH器件的设备ID信息;

5,如果读取ID正确,则调用 SPI_FLASH_SectorErase()把FLASH内容擦除,擦除后调用 SPI_FLASH_BufferWrite()向FLASH写入数据,然后再调用 SPI_FLASH_BufferRead()从刚刚写入的地址中读出数据,最后调用 Buffercmp()对写入和读取的数据进行匹配,匹配成功则把标志变量 TransferStatus1赋值为 PASSED(自定义的枚举变量);

6,根据标志量 TransferStatus1判断FLASH数据的:擦除,写入,读取是否正常,分情况输出到终端;

7,如果读取FLASH的ID信息错误,则直接向终端输出检测不到FLASH信息;

8,最后调用 SPI_Flash_PowerDown()函数关闭 FLASH设备的电源(因为数据写入到FLASH后并不会因断电而丢失,所以需要使用的时候再开启FLASH电源);

PS:

读取器件ID信息可以知道设备与主机是否能够正常工作,也便于区分不同的器件,可以在使用的FLASH用户数据手册找到ID表

SPI的初始化:

void SPI_FLASH_Init(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

/这里是GPIO初始化部分,将4个引脚都设定好/

/!《 Configure SPI_FLASH_SPI pins: SCK /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI pins: MISO /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI pins: MOSI /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI_CS_PIN pin: SPI_FLASH Card CS pin /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH(); //不用的时候就拉高

/这里是SPI设置部分/

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 3;

SPI_Init(SPI1, &SPI_InitStructure);

/* Enable SPI1 */

SPI_Cmd(SPI1, ENABLE);

}

GPIO初始化:

根据《STM32数据手册》以及《STM32参考手册》,把PA5(SCK),PA6(MISO),PA7(MOSI)设置成复用推挽输出,因为PA4(NSS)是使用软件模式,所以设置为通用退完输出。

SPI模式初始化:

对于初始化,是需要根据通讯的设备FLASH的SPI特性来决定的,下面成员分析:

SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;

这里设置通讯模式,这里设置成全双工模式(可以在keil环境下查找其他模式)

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

这里是设置工作模式,STM32的SPI设备可以gon工作在主机模式(SPI_Mode_Master)或从机模式(SPI_Mode_Slave),这两个模式最大的区别就是SPI的SCK信号线时序,SCK的时序是由通讯中的主机产生的,如果配置成从机模式,STM32的SPI模块将接收外来的SCK信号。(这里STM32作为SPI通讯主机,所以设置成 SPI_Mode_Master)。

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

这个是设置SPI每次通讯的数据大小(称为数据帧)为8位还是16位(从FLASH的数据手册可以查到,这里的FLASH的数据帧大小为8为,所以要把STM32的SPI模块设置相同的)

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;&SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

这两个成员是配置SPI的时钟极性(CPOL)和时钟相位(CPHA),这两个配置影响到SPI的通讯模式,要设置成符合将要互相通讯的设备的要求。

CPOL:可以取 SPI_CPOL_High(SPI 通讯空闲时 SCK 为高电平)或者SPI_CPOL_Low(SPI 通讯空闲时 SCK 为低电平);

CPHA:可以取 SPI_CPHA_1Edge(在 SCK 的奇数边沿采集数据)或者SPI_CPHA_2Edge (在 SCK 的偶数边沿采集数据);

查询这个FLASH的使用手册,可以了解到这个FLASH支持以SPI的模式0和模式3通讯。

模式0:在SPI空闲时,SCK为低电平,奇数边沿采样;

模式3:在SPI空闲时,SCK为高电平,偶数变异采样;

所以这里配置成模式3,把CPOL赋值为SPI_CPOL_High(SPI空闲时SCK为高电平),把CPHA赋值为SPI_CPHA_2Edge(在SCK的偶数边沿超级数据)

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

这里是配置NSS引脚的使用模式,可以选择为硬件模式(SPI_NSS_Hard)与软件模式(SPI_NSS_Soft),在硬件模式中的SPI片选由硬件自动产生,而软件模式则需要手动把相应的FPIO端口拉高或拉低产生非片选和片选信号(如果外界条件允许,硬件模式还会自动将STM32的SPI设置为主机)

这里是由软件产生模式,所以赋值为SPI_NSS_Soft.

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

这里是设置波特率分频值,分频后的时钟为SPI的SCK信号线的时钟频率,这个成员可以设置为fpclk的2,4,6,8,32,64,128,256分频。这里设置为4分频

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

所有串行的通讯协议都会有MSB先行(高位数据在前)还是LSB先行(地位数据在前)的问题,STM32的SPI模块可以通过对这个结构体成员,对这个特性编程控制。

根据FLASH的通讯时序,这里设置为MSB先行(SPI_FirstBit_MSB)

SPI_InitStructure.SPI_CRCPolynomial = 3;

这里是设置SPI的CEC校验的多项式,如果使用到CRC校验时,就是用这个成员的参数(多项式),来计算CRC的值。(这里的FLASH不支持CRC校验,所以赋值为3其实没意义)

配置完这些结构体成员后,调用 SPI_Init()把这些参数写入到寄存器中,然后调用SPI_Cmd()使能SPI1外设。

PS:

SPI_FLASH_CS_HIGH()这个实际是上一个自定义的宏:

#define SPI_FLASH_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4)

实际上这个宏就是用来把 PA4(NSS)引脚拉高,从而禁止SPI通讯

#define SPI_FLASH_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4)

如果要需要使用的时候,就直接拉低就行了这样就可以开始通讯了

控制FLASH的命令:

因为不同的设备,都会相应的有不同的指令,如 EEPROM 中会把第一个数据解释为存储矩阵的地址(实质就是指令)。而 FLASH 则定义了更多的指令,有写指令,读指令,读 ID 指令等等。

这些指令,对主机来说,只是它遵守最基本的通讯协议发送出的数据。但设备把这些数据解释成不同的意义(指令编码),所以才成为指令。在我们配置好 STM32 的协议模块后,想要控制设备,就要遵守相应设备所定义的命令规则。

指 令 表 中 的 A0~A23 指 地 址 ; M0~M7 为 器 件 的 制 造 商 ID(MANUFACTURER ID);D0~D7 为数据。

读取FLASH ID:

在命令列表可以了解到读取设备 ID 的命令(Device ID)编码为 ABh、dummy、dummy、dummy。表示此命令由这四个字节组成,其中dummy意为任意编码,即这几个字节必须发送数据,但这些数据是任意的,命令列表中带括号的字节数据表示由FLASH返回给主机的响应,可以看到Device ID命令的第5个字节为从机返回的响应,(ID7~ID0),即返回设备的ID号。

使用DeviceID命令时的时序图

可以看到主机首先通过MOSI线(即FLASH的DIO线)发送第一个字节为ABh编码,紧接着三个字节的dummy编码,然后FLASH就忽略DIO线上的信号,通过MISO线(即FLASH的DO线)把它的FLASH设备ID发送给主机。

u32 SPI_FLASH_ReadDeviceID(void)

{

u32 Temp = 0;

/使用的时候就拉低/

SPI_FLASH_CS_LOW();

/* Send “RDID ” instruction */

SPI_FLASH_SendByte(W25X_DeviceID);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

/* Read a byte from the FLASH */

Temp = SPI_FLASH_SendByte(Dummy_Byte);

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

return Temp;

}

SPI_FLASH_CS_LOW();

把片选拉低,开始通讯

SPI_FLASH_SendByte(W25X_DeviceID);

向FLASH发送一个命令字节编码:W25X_DeviceID (这里定义的宏为:0XAB)

SPI_FLASH_SendByte(Dummy_Byte);

根据指令表,发送完指令后,后面要接着发送三个字节的dummy_Byte(这里宏定义为:0xff,设置为其他也无所谓)

Temp = SPI_FLASH_SendByte(Dummy_Byte);

在前面发送完三个字节的 Dummy_Byte后,在第五个字节,FLASH通过DIO端口输出它的器件ID,所以这里再调用一次SPI_FLASH_SendByte(Dummy_Byte)接收返回值,赋值给Temp.

SPI_FLASH_CS_HIGH();

把片选拉高,结束通讯

这样就完成了读取FLASH ID,这里有一个相对底层的函数SPI_FLASH_SendByte(),它实现了利用SPI发送和接收数据的功能

u8 SPI_FLASH_SendByte(u8 byte)

{

/等待发送数据寄存器清空/

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

/发送数据/

SPI_I2S_SendData(SPI1, byte);

/等待接收数据寄存器为非空/

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

/返回接收到的数值/

return SPI_I2S_ReceiveData(SPI1);

}

流程:

1,调用库函数 SPI_I2S_GetFlagStatus()等待发送数据寄存器清空;

2,发送数据寄存器准备好后,调用库函数SPI_I2S_SendData()向从机发送数据;

3,调用库函数SPI_I2S_GetFlagStatus()等待接收数据寄存器非空;

4,接收寄存器非空时,调用SPI_I2S_ReceiveData()获取接收寄存器中的数据并作为函数的返回值,这个数据即由从机发送给主机的数据;

这是最底层的发送数据和接收数据的函数,利用了库函数的标志检测确保通讯正常。

读取厂商ID:

u8 SPI_FLASH_ReadID(void)

{

u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

SPI_FLASH_CS_LOW();//拉低开始通讯

SPI_FLASH_SendByte(W25x_JedecDeviceID);

Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

Temp = (Temp0 《《 16) | (Temp1 《《 8) | (Temp2);

SPI_FLASH_CS_HIGH();

return Temp;

123456789101112131415

}

这个函数和之前的读取设备ID流程也是类型的,差别在于发送一个字节的命令编码JEDEC ID(9Fh)之后,从机就通过D0线返回厂商ID以及0~16位的设备ID。

读厂商ID时序图

擦除FLASH内容:

扇区擦除(根据FLASH的储存原理,在写入数据前,要先对存储区域进行擦除,也叫预写)

void SPI_FLASH_SectorErase(u32 SectorAddr)

{

/写使能并且判断FLASH状态/

SPI_FLASH_WriteEnable();

SPI_FLASH_WaitForWriteEnd();

/*这里开始是FLASH擦除操作*/

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_SectorErase);

/*这里是擦除一个扇区,也就是4KB*/

SPI_FLASH_SendByte((SectorAddr & 0xFF0000) 》》 16);

SPI_FLASH_SendByte((SectorAddr & 0xFF00) 》》 8);

SPI_FLASH_SendByte(SectorAddr & 0xFF);

SPI_FLASH_CS_HIGH();

/*再次判断FLASH状态确保可以执行下一次操作*/

SPI_FLASH_WaitForWriteEnd();

1234567891011121314

}

这是扇区擦除时序,其中的第一个字节为扇区擦除命令编码(20h),紧跟其后的为要进行擦除的,根据FLASH的说明,整个存储矩阵分为块区和扇区,每块(Block)的大小为64KB,每个扇区(Sector)的大小为4KB,对存储矩阵进行擦除时,最小的单位为扇区。

写使能:

void SPI_FLASH_WriteEnable(void)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_WriteEnable);

SPI_FLASH_CS_HIGH();

123

}

这里根据写使能命令时序,只要发送命令WriteEnable(06h)就行了。

读FLASH状态:

在擦除操作之前,需要调用SPI_FLASH_WaitForWriteEnd()来确保FLASH不忙碌的时候,才发送命令或者数据,通过读取FLASH的状态寄存器来获知他的工作状态。

void SPI_FLASH_WaitForWriteEnd(void)

{

u8 FLASH_Status = 0;

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadStatusReg);

/*一直检测FLASH状态寄存器状态,直到Bit0位(BUSY位)为0)*/

do

{

FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);

}while((FLASH_Status & WIP_Flag) == SET);

SPI_FLASH_CS_HIGH();

12345678910

}

整个函数实质是不断的循环检测FLASH状态寄存器的Busy位,知道FLASH的内部写时序完成,从而确保下一通讯操作正常。主机通过发送读状态寄存器命令Read Status Register(05h 编码),返回的为他的8为状态寄存的值。

检测FLASH的状态寄存器的Bit0(BUSY位),当FLASH在执行内部写时序的时候,除了读状态寄存器命令,其他的一切命令他都会忽略,并且BUSY位保持为1,所以我们需要等待BUSY位为0的时候,再向FLASH发送其他命令。

向FLASH写入数据:

对FLASH写入数据,最小单位是256字节,厂商把这个单位曾为页。写入时,一般也只有页写入的方式,所以为了方便的把一个很长的数据写入到FLASH时,一般需要进行转换,把数据按页分好,再写入到FLASH中(类似于I2C对EEPROM的页写入,只是页的大小不同而已)。

void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

{

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;

if (Addr == 0)

{

if(NumOfPage == 0)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

}

else

{

while(NumOfPage--)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

else

{

if(NumOfPage == 0)

{

if(NumOfSingle 》 count)

{

temp = NumOfSingle - count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);

}

else

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite)

}

}

else

{

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_PageS

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

if(NumOfSingle != 0)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

}

对数组进行分页后,就调用SPI_FLASH_PageWrite()对数据进行按页写入(是不是和I2C写入EEPROM的写函数一样(连行数都差不多-_-!),不了解的话可以去看之前的I2C部分)

底层写操作:SPI_FLASH_PageWrite()

void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

{

/写使能/

SPI_FLASH_WriteEnable();

/拉低开始通讯/

SPI_FLASH_CS_LOW();

/发送PageProgram(02h))/

SPI_FLASH_SendByte(W25X_PageProgram);

/* Send WriteAddr high nibble address byte to write to */

SPI_FLASH_SendByte((WriteAddr & 0xFF0000) 》》 16);

/* Send WriteAddr medium nibble address byte to write to */

SPI_FLASH_SendByte((WriteAddr & 0xFF00) 》》 8);

/* Send WriteAddr low nibble address byte to write to */

SPI_FLASH_SendByte(WriteAddr & 0xFF);

/这里是判断写大小是否符合FLASH规定的256/

if(NumByteToWrite 》 SPI_FLASH_PerWritePageSize)

{

NumByteToWrite = SPI_FLASH_PerWritePageSize;

//printf(“\n\r Err: SPI_FLASH_PageWrite too large!”);

}

/这里才是写真是数据/

while (NumByteToWrite–)

{

/* Send the current byte */

SPI_FLASH_SendByte(*pBuffer);

/* Point on the next byte to be written */

pBuffer++;

}

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

/* Wait the end of Flash writing */

SPI_FLASH_WaitForWriteEnd();

}

发送完写入命令Page Program(编码 02h)及地址之后,可以连续写入最多256个字节的数据(SPI_FLASH_PerWritePageSize = 256),在发送完数据之后,记得调用SPI_FLASH_WaitForWriteEnd()等待FLASH内部写时序完成再推出函数。

从FLASH读取数据:

对于读取数据,发送一个命令后,可以无限制的一直把整个FLASH的数据都读取完,直到读取的数据量足够了,就拉高片选信号以表示读取数据结束。

void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadData);

/* Send ReadAddr high nibble address byte to read from */

SPI_FLASH_SendByte((ReadAddr & 0xFF0000) 》》 16);

/* Send ReadAddr medium nibble address byte to read from */

SPI_FLASH_SendByte((ReadAddr& 0xFF00) 》》 8);

/* Send ReadAddr low nibble address byte to read from */

SPI_FLASH_SendByte(ReadAddr & 0xFF);

while (NumByteToRead–)

{

/* Read a byte from the FLASH */

*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);

/* Point to the next location where the byte read will be saved*/

pBuffer++;

}

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

}

首先发送一个读取数据命令 Read Data(03h),接着发送24位读数据起始地址,STM32再通过D0线接收数据,并使用指针的方式记录起来


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FlaSh
    +关注

    关注

    10

    文章

    1549

    浏览量

    146643
  • SPI
    SPI
    +关注

    关注

    17

    文章

    1614

    浏览量

    89586
收藏 人收藏

    评论

    相关推荐

    STM32F207擦除片内FLASH,退出DEBUG无法执行下一步程序是怎么回事?

    由于项目需求,需要擦除片内指定空间,然后从SPI_FLASH中加载程序运行 问题如下:: 在DEBUG模式下,执行擦除程序后,则退出DEBUG,无法执行下一步程序 擦除代码如下: 1
    发表于 04-23 07:46

    FATFS对SPI_FLASH新建文件、删除文件或者修改文件后电脑无法识别,为什么?

    FATFS对SPI_FLASH新建文件、删除文件或者修改文件后电脑无法识别,而且会断开连接,需要重新插拔,这是什么问题呢
    发表于 04-09 07:06

    STM32F407ZGT6 spi flash片选引脚无法被拉低的原因?怎么解决?

    我用的芯片是STM32F407ZGT6,RTThread版本5.0.2,spi flash挂载在spi1总线,设备号是spi10 int
    发表于 02-20 07:13

    stm32 flash写数据怎么存储的

    stm32 flash写数据怎么存储的  STM32是一款广泛应用于嵌入式系统开发的微控制器,它的Flash存储器是其中一个重要的组成部分。在本文中,我将详细介绍
    的头像 发表于 01-31 15:46 757次阅读

    STM32Flash写了保护怎么办?STM32如何设置读保护和解除读保护?

    STM32Flash写了保护怎么办?STM32如何设置读保护和解除读保护? 一、STM32Flash写了保护怎么办? 当
    的头像 发表于 10-29 17:24 6722次阅读

    为什么STM32Flash地址要设置到0x08000000?

    为什么STM32Flash地址要设置到0x08000000?
    的头像 发表于 10-26 15:50 980次阅读
    为什么<b class='flag-5'>STM32</b>的<b class='flag-5'>Flash</b>地址要设置到0x08000000?

    STM32实例-待机唤醒实验

    STM32实例-待机唤醒实验
    的头像 发表于 10-26 15:48 1170次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>实例</b>-待机唤醒实验

    STM32F767ZGT6连续调用两次rt_spi_transfer读取同一个地址,第二次读取的时反馈异常变为0怎么解决?

    连续调用两次rt_spi_transfer读取同一个地址,第二次读取的时候反馈异常变为0. 前提信息: 1.芯片采用STM32F767ZGT6 2.使用的rt_spi_send进行写
    发表于 08-20 15:24

    STM32 SPI基础内容

    、存储芯片、温度传感器等众多器件都有使用SPI接口通信。 这些器件通常作为从设备,单片机作为主设备来控制它们,今天就结合STM32来分析一下SPI常见通信有异常的问题。 STM32
    的头像 发表于 08-19 09:14 2579次阅读
    <b class='flag-5'>STM32</b> <b class='flag-5'>SPI</b>基础内容

    如何用CK6865E芯片设计成蓝牙音响背景音播放或者做成睡眠音响?

    睡眠助眠音响的硬件设计方案,SPI_FLASH方式存储更方便
    的头像 发表于 08-17 14:31 370次阅读
    如何用CK6865E芯片设计成蓝牙音响背景音播放或者做成睡眠音响?

    应用笔记 | 关闭SPI会导致WRPERR错误的问题分析

    关键字:SPIFlash,WRPERR 目录预览 1 引言  2 问题 3 问题解决 4 小结 0 1 引言 在STM32的应用中,SPI算是用的比较多的外设了,也是单片机最常见外设
    的头像 发表于 08-10 18:45 534次阅读
    应用笔记 | 关闭<b class='flag-5'>SPI</b>会导致WRPERR错误的问题分析

    STM32F1的SPI模块协议介绍

    SPI是是一种高速的,全双工,同步的总线通信方式。STM32F1低中容量设备的SPI模块支持主从两种模式。
    的头像 发表于 07-24 15:32 2160次阅读
    <b class='flag-5'>STM32</b>F1的<b class='flag-5'>SPI</b>模块协议介绍

    STM32SPI控制器实现SPI

    SPI是一种高速的,全双工,同步的通信总线,原理和使用简单,占用引脚资源少,是一种常用的通信方式。STM32通常有2~3个SPI接口。
    发表于 07-22 11:47 1783次阅读
    <b class='flag-5'>STM32</b>的<b class='flag-5'>SPI</b>控制器实现<b class='flag-5'>SPI</b>

    基于FPGA的SPI Flash控制器的设计方案

    一个基于FPGA的SPI Flash读写硬件实现方案,该方案利用硬件对SPI Flash进行控制,能够非常方便地完成Flash的读写、擦除、
    的头像 发表于 07-15 16:55 1293次阅读
    基于FPGA的<b class='flag-5'>SPI</b> <b class='flag-5'>Flash</b>控制器的设计方案

    STM32CubeMx入门教程(6):SPI读写FLAH的应用

    导语“本教程将使用CubeMX初始化SPI,使用SPI对W25Q64 FLASH进行读写操作,通过HAL库的读写应用来数据FLASH的操作细节。”
    发表于 07-12 11:32 1378次阅读
    <b class='flag-5'>STM32</b>CubeMx入门教程(6):<b class='flag-5'>SPI</b>读写FLAH的应用