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

目录

1. W25Q32JVSSIQ背景知识

1.1 64个可擦除块

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

1.3 页


1. W25Q32JVSSIQ背景知识

        

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

1.1 64个可擦除块

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

  

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

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

1.3 页

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

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; bytesRead += sizeof(buffer))
    {
        uint32_t toRead = sizeof(buffer);
        if (bytesRead + toRead > length) {
            toRead = length - bytesRead;
        }

        // 读取地址开始的若干字节数据
        SPI_FLASH_BufferRead(addr + bytesRead, buffer, toRead);

        // 检查读取的数据是否为0xFF
        for (uint32_t i = 0; i < toRead; i++)
        {
            if (buffer[i] != 0xFF)
            {
                return -1; // 如果不是0xFF,返回false,表示擦除失败
            }
        }
    }
    return 0; // 如果所有数据都是0xFF,返回true,表示擦除成功
}




/*清空扇区*/
int SPI_FLASH_SectorErase(uint32_t addr)
{
    cs_low();
	/* 开始的时候要先发送写使能信号 */
	SPI1_FLASH_WriteEnable();
	

	/* 发送扇区擦除命令 */
	SPI_FLASH_SendByte(0x20);
	/* 发送扇区地址,高位先行 */
	SPI_FLASH_SendByte( (addr & 0xff0000) >> 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 < size; i++)
	{
		SPI_FLASH_SendByte(pBuffer[i]); // 发送数据
	    printf("%02X ", pBuffer[i]);
	}
	printf("\n");
	cs_high();
	SPI1_FLASH_WaitEnd();
}


/* 读取数据,读取指定地址制定长度的数据 */
void SPI_FLASH_BufferRead(uint32_t addr, uint8_t *pBuffer, uint16_t size)
{
	cs_low();
	/* 发送读取命令 */
	SPI_FLASH_SendByte(0x03);
	/* 发送要读取的地址,高位先行*/
	SPI_FLASH_SendByte( (addr & 0xff0000) >> 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 < size; i++)
	{
		pBuffer[i] = SPI_FLASH_SendByte(Dummy_Byte); // 发送数据

	}
	cs_high();

}

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)函数

读取寄存器最低位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 运行结果

 

  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在您提供的引用中,您提到了一些与STM32F429相关的内容,包括STM32F429挑战者开发板、Keil 5、pack包、Microlib、SDRAM、W9825G6屏幕、GT9147触摸屏、SD卡、SPIFLASH等。关于您的问题,STM32F429IGT6是一款基于ARM Cortex-M4内核的STM32系列微控制器,它支持SPI接口。SPI(Serial Peripheral Interface)是一种串行外设接口,用于在微控制器和外部设备之间进行通信。通过SPI接口,您可以连接各种外设,如传感器、存储器等。在使用STM32F429IGT6的SPI功能时,您需要进行相应的配置,包括设置SPI的时钟频率、数据位宽、传输模式等。具体的配置过程可以通过使用STM32CubeMX来完成,它是一款由STMicroelectronics提供的图形化配置工具,可以帮助您生成初始化代码并进行硬件配置。在配置SPI时,您需要选择相应的引脚和功能,并设置相关的参数。希望这些信息对您有帮助。 #### 引用[.reference_title] - *1* *3* [STM32F429IGT6项目准备1——使用STM32CubeMX初始化并重定义串口](https://blog.csdn.net/qq_42039294/article/details/112169214)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32F429IGT6入门(一)](https://blog.csdn.net/Alkaid2000/article/details/119988434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值