SPI协议——对外部SPI Flash操作

06-20 1172阅读

目录

1. W25Q32JVSSIQ背景知识

1.1 64个可擦除块

1.2 1024个扇区(每个块有16个扇区)

1.3 页


1. W25Q32JVSSIQ背景知识

        SPI协议——对外部SPI Flash操作

W25Q32JV阵列被组织成16,384个可编程页,每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除)、128组(32KB块删除)、256组(64KB块删除)或整个芯片(芯片清除)。W25Q32JV分别有1,024个可擦除扇区和64个可擦除块。小型的4KB扇区允许在需要数据和参数存储的应用程序中具有更大的灵活性。

1.1 64个可擦除块

8M的空间被切割成128块,每块64kb

  SPI协议——对外部SPI Flash操作

1.2 1024个扇区(每个块有16个扇区)

每块64kb又每切割成16个扇区,每扇区4kb(16*4=64kb)

SPI协议——对外部SPI Flash操作

1.3 页

一扇区是4KB,划分成16份每份256字节这就是页,而且擦除数据也只能按照扇区或者块来擦除,下图的00FF00H-00FFFFH刚好256字节,000000h-0000ff之间也是256字节

SPI协议——对外部SPI Flash操作

2. Flash操作注意事项

2.1 写入操作

1. 写入操作之前必须先进行写使能;

2. 每个数据只能由1改写为0,不能由0改写为1是因为FLASH芯片自身的限制决定,它没有完全任意修改的能力,所以芯片内部无数据的时候默认为0XFF,表示为空。;

3. 写入数据之前必须先进行擦除,擦除后所有的数据位为1;

4. 进行擦除时必须按照最小擦除单元进行擦除(最小擦除单位为扇区);

5.连续写入多字节数据时,最多写入一页的数据,如果超过一页则会覆盖前面写的内容;

6. 写入操作结束后, 芯片进入忙状态(寄存器中有一个busy位),不影响新的读写操作;

2.2 读操作

进行读操作时,直接调用读取操作时序,无需使能,但是别忘了拉低电平,没有页的限制,读取之后也不会进入到忙状态,但是不能在忙状态时读取数据;

3.代码编写

此次对SPI Flash进行读写操作还是采用SPI1,具体的配置请见上篇文章,在这里不做详细概述。

3.1 spi_flash.c添加代码

/* Flash 写使能 */
static void SPI1_FLASH_WriteEnable(void)
{
	cs_low();
	SPI_FLASH_SendByte( 0x06 ); //需要查芯片的datasheet来看具体的
	cs_high();
}
/* Flash 等待写结束 */
static void SPI1_FLASH_WaitEnd(void)
{
	uint8_t	state = 0;
	cs_low();
	SPI_FLASH_SendByte(0x05); //需要查芯片的datasheet来看具体的
	do
	{
		state = SPI1_FLASH_ReadByte();
	}
	while( (state & 0x01) == SET );
	cs_high();
}
// 扇区擦除验证函数
int SPI_FLASH_VerifyErase(uint32_t addr, uint32_t length)
{
    uint8_t buffer[16];
    uint32_t bytesRead;
    for (bytesRead = 0; bytesRead  length) {
            toRead = length - bytesRead;
        }
        // 读取地址开始的若干字节数据
        SPI_FLASH_BufferRead(addr + bytesRead, buffer, toRead);
        // 检查读取的数据是否为0xFF
        for (uint32_t i = 0; i > 16 );
	SPI_FLASH_SendByte( (addr & 0xff00) >> 8 );
	SPI_FLASH_SendByte( addr & 0xff );
	cs_high();
	/* 最后等待Flash 处理完这次信号之后退出 */
	SPI1_FLASH_WaitEnd();
	 if (!SPI_FLASH_VerifyErase(addr, 4096)) // 通常扇区大小为4KB
	    {
	        printf("Sector erase failed at address 0x%06X\n", addr);
	    }
	    else
	    {
	        printf("Sector erase successful at address 0x%06X\n", addr);
	    }
	return 0;
}
/* 按页写数据,在写数据之前要先擦除 */
void SPI_FLASH_PageWrite(uint32_t addr, uint8_t *pBuffer, uint8_t size)
{
/* 发送使能信号 */
	SPI1_FLASH_WriteEnable();
	cs_low();
	/* 发送页写入命令 */
	SPI_FLASH_SendByte(0x02);
	/* 发送要写入的地址,高位先行 */
	SPI_FLASH_SendByte( (addr & 0xff0000) >> 16 );
	SPI_FLASH_SendByte( ( addr & 0xff00) >> 8 );
	SPI_FLASH_SendByte(addr & 0xff );
	printf("Writing to address 0x%06X: ", addr);
	for (uint8_t i = 0; i > 16 );
	SPI_FLASH_SendByte( (addr & 0xff00 ) >> 8 );
	SPI_FLASH_SendByte(addr & 0xff );
	printf("Reading to address 0x%06X: ", addr);
	/* 逐位读取数据到指针上 */
	for (uint8_t i = 0; i  

3.2 详细分析

1. 地址分解

假设 addr 是一个24位的地址(0xFFFFFF范围内)。发送前需要将addr分解成三个8位字节,因为SPI通常以字节为单位发送数据。

SPI_SendData((addr & 0xff0000) >> 16);
  • addr & 0xff0000:使用位与操作(&)保留 addr 的高8位,其余位清零。
  • >> 16:将结果右移16位,使得高8位移到最低8位的位置。
  • SPI_SendData():将移位后的结果发送出去,这发送的是 addr 的高8位。
  • 例如:如果 addr = 0x123456,那么 (addr & 0xff0000) = 0x120000,右移16位得到 0x12,即发送的第一个字节是 0x12。

     其他同理

    2. SPI1_FLASH_WaitEnd(void)函数

    SPI协议——对外部SPI Flash操作

    读取寄存器最低位BUSY的状态,如果为1表示忙,0表示空闲;

    3.3 main.c函数

    uint8_t Rx[100];
    uint8_t Tx[] = "Hello!", n;
    SPI_FLASH_SectorErase(0x00000);
    n=sizeof(Tx) -1 ;
    SPI_FLASH_PageWrite(0x00000 ,Tx ,n);
    SPI_FLASH_BufferRead(0x00000 ,Rx ,n);
    printf("the data is %s\n", Rx);
    

    3.4 运行结果

    SPI协议——对外部SPI Flash操作 

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]