STM32H723+SPI+EEPROM调试记录(EEP型号:SST26VF016B)

1 篇文章 0 订阅
1 篇文章 0 订阅

     首先说说什么是EEPROM,EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory,是一种掉电后数据不丢失的存储芯片。总的来说和FLASH芯片提供的功能相似,但是操作方式有些许不同。

1.读取方式

        Flash和EEPROM都采用随机读取,可以通过地址直接访问存储器中的数据。

2.写入方式

        Flash和EEPROM的写入方式不一样,EEPROM可以按字节进行写入也可按页写入(256字节),而Flash通常需要按块进行写入。还有就是,在Flash中,要写入一个数据,需要先擦除一整个块,然后再将新数据写入该块。

3.擦除方式

        Flash和EEPROM的擦除方式不一样,EEPROM可以按字节进行擦除(SST26似乎也只能块擦除,这个可能得根据EEP型号而论),而Flash一般需要按块进行擦除。也就是说,在Flash中,要擦除一个数据,通常需要先擦除一整个块,然后再将该块中需要保留的数据重新写入,比EEPROM操作麻烦一些。

4.擦写速度

        EEPROM的擦写速度普遍比Flash慢一些,擦写速度会受到许多因素的影响,包括使用的存储器型号、使用的接口类型、写入和擦除的数据量、芯片温度等等。

一、SST26基本信息

1、如下引脚图可见,该芯片可以使用两种通讯方式,SPI和QSPI,当使用SPI模式时引脚功能如下所示:

        2:MISO                       6:时钟线

        5:MOSI                       1:片选

当使用QSPI模式时引脚功能如下所示:

        1:片选                         2:SIO1                          7:SIO3    

        5:SIO0                        3:SIO2                          6:时钟线

由这四\六个引脚配合stm32完成SPI\QSPI通讯。

        手册上值得注意的地方一定要好好看:

        关于HOLD#这个引脚只在SPI模式下不使用,一定要拉高,不然什么都读不到。

        上图是EEP的内存划分,一个扇区是4096字节,一页是256字节,至于块划分有8k、32k、64k。

        上图是SST26的指令图,就算是使用其他型号的EEP,它的手册里面也会有这样一张表,其中Command Cycle就需要写入EEP的命令注意,如“00H”,这个H不是命令,是表示16进制,实际命令是“00”。

        还有一点也要注意,有的指令只在QSPI/SPI上生效,一定要注意辨别。

二、EEP驱动编写

        首先说说驱动的意义,所谓驱动其实就是写寄存器命令,但是我们总不能每次向EEP写入数据或者擦除数据,都重复写一次寄存器,这样太过于繁琐,也容易出错,因此我们需要将某个操作,需要写入那些命令,按照顺序封装成函数供自己调用。

      说实话驱动编写这一块的C语言逻辑难度一般,稍有些功底就可以完成,就是一定要看明白手册,有时候你写某一命令,可能需要一些前置条件,寄存器命令的写如顺序,时间间隔等等都需要细读手册,所以说驱动开发的难度几乎都在英文手册的阅读这一块了。

        我所编写EEP驱动也是根据网上现有的类似型号的EEPROM修改的,话不多说,直接上代码:

EEP.h如下,主要是对指令进行了一些封装以及一些驱动函数声明:

/*
 * @Descripttion: 
 * @version: 
 * @Author: HuangLR
 * @Date: 2023-10-23 11:35:04
 * @LastEditors: smile
 * @LastEditTime: 2023-10-27 14:31:33
 */

#ifndef _EEP_H_
#define _EEP_H_
#include "main.h"
#include "spi.h"

#define CMD_JEDECID		0x9F	//获取芯片ID号
#define CMD_HS_READ		0x0B	//高速读
#define CMD_READ		0x03	//读
#define CMD_WREN		0x06	//写使能
#define CMD_WRDISEN     0x04    //写失能
#define CMD_SE			0x20	//扇区擦除
#define CMD_BE			0xD8	//块擦除
#define CMD_CE			0xC7	//芯片擦除
#define CMD_PP			0x02	//页编程(写)
#define CMD_RDSR		0x05	//读状态寄存器(BUSY位)
#define CMD_RBPR		0x72	//读块保护寄存器
#define CMD_ULBPR		0x98	//解锁块保护
#define CMD_SFPD        0x5A    //获取SFPD只读区域数据

#define MAC_STATE       0x000260    //MAC编程状态地址
#define ADD_MAC         0x000261    //MAC首地址        


#define MF_ID		0xBF
#define DEV_TYPE	0x26
#define DEV_ID		0x41
#define JEDEC_ID	(uint32_t)((MF_ID<<16)|(DEV_TYPE<<8)|DEV_ID)

#define  MAC_Num    6             //MAC地址长度
/
static void EEPStart();

static void EEPStop();

static uint8_t EEPWriteByte(uint8_t txBuf);

static uint8_t EEPReadByte(void);

uint32_t EEPReadID(void);

uint8_t EEPReadStatusRegister(void);

void EEPHighRead(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum);

void EEPRead(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum);
	
static void EEPWriteEN(void);

void EEPSectorErase(uint32_t addr);

void EEPBlockErase(uint32_t addr);

void EEPChipErase(void);

void EEPPageProgram(uint32_t addr, uint8_t *txBuf, uint8_t wrNum);

void EEPReadBlockProtectionRegister(uint8_t *rxBuf);

void EEPUnlockBlockProtection(void);

void EEPTASK(void);

void EEPGetSFDP(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum);
#endif

EEP.C如下:

/*
 * @Descripttion: 
 * @version: 
 * @Author: HuangLR
 * @Date: 2023-10-23 11:34:38
 * @LastEditors: smile
 * @LastEditTime: 2023-10-27 14:32:12
 */

#include "EEP.h"

static void EEPStart()
{
	HAL_GPIO_WritePin(EEP_CS_GPIO_Port, EEP_CS_Pin, GPIO_PIN_RESET);
}

static void EEPStop()
{
	HAL_GPIO_WritePin(EEP_CS_GPIO_Port, EEP_CS_Pin, GPIO_PIN_SET); 
}

static uint8_t EEPWriteByte(uint8_t txBuf)
{
	uint8_t rxBuf = 0;
	if(HAL_SPI_TransmitReceive(&hspi5, &txBuf, &rxBuf, 1, 500) != HAL_OK)
	{
		return 0;
	}
	return rxBuf;
}

static uint8_t EEPReadByte(void)
{
	return EEPWriteByte(0xFF);
}

uint32_t EEPReadID(void)
{
	uint8_t rxBuf[3];
	EEPStart();
	EEPWriteByte(CMD_JEDECID);
	rxBuf[2] = EEPReadByte();
	rxBuf[1] = EEPReadByte();
	rxBuf[0] = EEPReadByte();
	EEPStop();
	return (uint32_t)((rxBuf[2]<<16) | (rxBuf[1]<<8) | rxBuf[0]);
}

 uint8_t EEPReadStatusRegister(void)
{
	uint8_t rxBuf;
	EEPStart();
	EEPWriteByte(CMD_RDSR);
	rxBuf = EEPReadByte();
	EEPStop();
	return rxBuf;
}

void EEPHighRead(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum)
{
	EEPStart();
	EEPWriteByte(CMD_HS_READ);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	EEPWriteByte(0xff);
	while(rdNum--)
	{
		*rxBuf++ = EEPReadByte();		
	}
	EEPStop();
}

void EEPRead(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum)
{
	EEPStart();
	EEPWriteByte(CMD_READ);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	while(rdNum--) 
	{
		*rxBuf++ = EEPReadByte();
	}
	EEPStop();
}

static void EEPWriteEN(void)
{
	EEPStart();
	EEPWriteByte(CMD_WREN);
	EEPStop();
}

void EEPSectorErase(uint32_t addr)
{
	EEPWriteEN();
	EEPStart();
	EEPWriteByte(CMD_SE);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	EEPStop();
	while((EEPReadStatusRegister() & 0x01) != 0);
}

void EEPBlockErase(uint32_t addr)
{
	EEPWriteEN();
	EEPStart();
	EEPWriteByte(CMD_BE);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	EEPStop();
	while((EEPReadStatusRegister() & 0x01) != 0);
}

void EEPChipErase(void)
{
	EEPWriteEN();
	EEPStart();
	EEPWriteByte(CMD_CE);
	EEPStop();
	while((EEPReadStatusRegister() & 0x01) != 0);
}

void EEPPageProgram(uint32_t addr, uint8_t *txBuf, uint8_t wrNum)
{
	EEPWriteEN();
	EEPStart();
	EEPWriteByte(CMD_PP);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	while(wrNum--) 
	{
		EEPWriteByte(*txBuf++);
	}
	EEPStop();
	while((EEPReadStatusRegister() & 0x01) != 0);
}


void EEPReadBlockProtectionRegister(uint8_t *rxBuf)
{
	uint8_t rdNum = 10;
	EEPStart();
	EEPWriteByte(CMD_RBPR);
	while(rdNum--) 		
	{
		*rxBuf++ = EEPReadByte();
	}
	EEPStop();
}

void EEPUnlockBlockProtection(void)
{
	EEPWriteEN();
	EEPStart();
	EEPWriteByte(CMD_ULBPR);
	EEPStop();
}

void EEPGetSFDP(uint32_t addr, uint8_t *rxBuf, uint32_t rdNum)
{
	EEPStart();
	EEPWriteByte(CMD_SFPD);
	EEPWriteByte((uint8_t)((addr>>16)&0xff));
	EEPWriteByte((uint8_t)((addr>>8)&0xff));
	EEPWriteByte((uint8_t)(addr&0xff));
	EEPWriteByte(0xff);
	while(rdNum--) 
	{
		*rxBuf++ = EEPReadByte();		
	}
	EEPStop();
}

        在此之前其实还有一个步骤,那就是初始化SPI接口,我习惯使用STM32CUBEX生成,减少新建工程的麻烦:

三、初始化SPI

我的配置如下,使用SPI模式0,时钟分频根据主时钟来,一般把SPI频率控制在4-8M即可。

四、读写EEP

void EEPTASK()
{
	uint32_t i,ID = 0;
	uint32_t addr = 0x100000;   //这个地址用户自己指定就行
	uint8_t txBuf[256], rxBuf[256], blockProtection[10], num;
	uint8_t ip1[2] = {0};
	uint8_t ip2[2] = {0};

	
	ID = EEPReadID();           //首先读取ID,验证通讯是否成功
	printf("EEPid =0x%x\r\n",ID);
	if(ID != JEDEC_ID )
	{
		ID = EEPReadID();
		while((ID != JEDEC_ID));
	}
	EEPReadBlockProtectionRegister(blockProtection); //取消写保护,一定要取消,不然无法写入 
                                                     //数据,具体写入那些命令驱动有详细讲。
	for(num = 0; num < 10; num++) 
	{
		if(blockProtection[num] != 0) 
		{
			EEPUnlockBlockProtection();
			break;
		}
	}

	EEPBlockErase(addr);   //擦除     
	for(i = 0; i < 100; i++) 
	{
		txBuf[i] = 4;
	}
	EEPPageProgram(addr, txBuf, 100);     //页写入,写入100(自动从该地址向后偏移)字节,单字节            
                                          //写入不会偏移
	EEPHighRead(addr, rxBuf, 100);        //高速读取该地址100(会自动偏移)字节。

}

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值