【本文发布于https://blog.csdn.net/Stack_/article/details/116353030,未经许可不得转载,转载须注明出处】
FM25W256
一、电路
二、配置SPI(GD32F303)
@ CSDN Tyrion.Mon
/**
* @brief 初始化SPI1
* @note SPI1_NSS -- PB12
* SPI1_SCK -- PB13
* SPI1_MI -- PB14
* SPI1_MO -- PB15
* @param None
* @retval None
* @author PWH @ CSDN Tyrion.Mon
* @date 2021/3
*/
void SPI1_Init(void)
{
RCU->RCU_APB1EN.Bits.SPI1EN = 1; //
/* SCK复用输出 */
Gd32f30x_Gpio_Init(GPIO_PB13, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
/* MI上拉输入 */
Gd32f30x_Gpio_Init(GPIO_PB14, GPIO_MODE_IN, GPIO_CTL_IPU);
/* MO复用输出 */
Gd32f30x_Gpio_Init(GPIO_PB15, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
/* SPI1 */
//1:主机模式
SPI1->SPI_CTL0.Bits.MSTMOD = 1;
//0: 2 线单向传输模式
SPI1->SPI_CTL0.Bits.BDEN = 0;
//0:全双工模式(当 BDEN 清零时)
SPI1->SPI_CTL0.Bits.RO = 0;
//0: 8 位数据帧格式
SPI1->SPI_CTL0.Bits.FF16 = 0;
//1: NSS 软件模式, NSS 电平取决于 SWNSS 位
SPI1->SPI_CTL0.Bits.SWNSSEN = 1;
SPI1->SPI_CTL0.Bits.SWNSS = 1;
//SPI1时钟来源:APB1 psc=001 : 4分频 FRAM spi最高20M
SPI1->SPI_CTL0.Bits.PSC = 1;
//0:先发送最高有效位
SPI1->SPI_CTL0.Bits.LF = 0;
//1: SPI 为空闲状态时, CLK 引脚拉低,此时fram spi工作在mode0
SPI1->SPI_CTL0.Bits.CKPL = 0;
//0:在第一个时钟跳变沿采集第一个数据
SPI1->SPI_CTL0.Bits.CKPH = 0;
//1: RBNE 中断使能。当 RBNE 置位时,产生中断。
// SPI1->SPI_CTL1.Bits.RBNEIE = 1;
//1: SPI 设备使能
SPI1->SPI_CTL0.Bits.SPIEN = 1;
}
三、命令字
所有写操作前都必须先发送WREN以使能写操作。
@ CSDN Tyrion.Mon
/**
* @brief 使能/使能写操作
* @note
* @param ENABLE 允许;DISABLE 禁止
* @retval None
* @author PWH @ CSDN Tyrion.Mon
* @date 2021/3
*/
static void FRAM_WriteEnable(uint8_t EN_DISEN)
{
FRAM_Select(CS_ENABLE);
if (EN_DISEN == ENABLE)
SPIx_SendData(SPI1, WREN); //使能写操作(WREN操作码使WEL置位,允许写操作)
else
SPIx_SendData(SPI1, WRDI);
FRAM_Select(CS_DISABLE);
}
四、读写
读写状态寄存器
@ CSDN Tyrion.Mon
/**
* @brief 读状态寄存器数据
* @note
* @param NONE
* @retval 状态寄存器值
* @author PWH@ CSDN Tyrion.Mon
* @date 2021/3
*/
static uint8_t FRAM_ReadSR(void)
{
uint8_t ret;
FRAM_Select(CS_ENABLE);
SPIx_SendData(SPI1, RDSR);
ret = SPIx_SendData(SPI1, 0x00);
FRAM_Select(CS_DISABLE);
return ret;
}
/**
* @brief 写状态寄存器
* @note
* @param 状态寄存器值
* @retval NONE
* @author PWH @ CSDN Tyrion.Mon
* @date 2021/3
*/
static void FRAM_WriteSR(uint8_t val)
{
FRAM_WriteEnable(ENABLE); //写SR前需要使能写操作
FRAM_Select(CS_ENABLE);
SPIx_SendData(SPI1, WRSR);
SPIx_SendData(SPI1, val);
FRAM_Select(CS_DISABLE);
}
读写存储块
/**
* @brief FRAM
* @note
* @param None
* @retval None
* @author PWH@ CSDN Tyrion.Mon
* @date 2021/3
*/
uint16_t FRAM_Write(uint16_t AddrOffset, uint8_t *Data, uint16_t DataLen)
{
if ((!DataLen) || (AddrOffset > FRAM_ADDR_MAX) || ((AddrOffset + DataLen - 1) > FRAM_ADDR_MAX))
return 0;
FRAM_WriteSR(0x80); //写SR寄存器,所有存储块取消写保护
FRAM_WriteEnable(ENABLE); //写存储块前需要使能写操作
FRAM_Select(CS_ENABLE);
SPIx_SendData(SPI1, WRITE); //写指令
SPIx_SendData(SPI1, (AddrOffset >> 8) & 0x00ff); //写入起始地址高字节
SPIx_SendData(SPI1, AddrOffset & 0x00ff); //写入起始地址低字节
do {
SPIx_SendData(SPI1, *Data++);
} while (--DataLen);
FRAM_Select(CS_DISABLE);
FRAM_WriteSR(0x8C); //写SR寄存器,所有存储块使能写保护
return 1;
}
/**
* @brief FRAM
* @note
* @param None
* @retval Noned
* @author PWH @ CSDN Tyrion.Mon
* @date 2021/3
*/
uint16_t FRAM_Read(uint16_t AddrOffset, uint8_t *Data, uint16_t DataLen)
{
if ((!DataLen) || (AddrOffset > FRAM_ADDR_MAX) || ((AddrOffset + DataLen - 1) > FRAM_ADDR_MAX))
return 0;
FRAM_Select(CS_ENABLE);
SPIx_SendData(SPI1, READ); //读指令
SPIx_SendData(SPI1, (AddrOffset >> 8) & 0x00ff); //写入起始地址高字节
SPIx_SendData(SPI1, AddrOffset & 0x00ff); //写入起始地址低字节
do {
*Data++ = SPIx_SendData(SPI1, 0x00);
} while (--DataLen);
FRAM_Select(CS_DISABLE);
return 1;
}
FM24CL04B/FM24CL16B
一、电路
/*
* FM24CL04B
* 1 0 1 0 A2 A1 A0 R/W
*
* A2 A1 已硬件拉低, A0作页选择
*
* FM24CL16B
* 1 0 1 0 A2 A1 A0 R/W
*
* 外部无地址选择引脚,A2 A1 A0作页选择
*
*/
二、I2C驱动 (软 – ESP8266)
@ CSDN Tyrion.Mon
#define I2C_MASTER_SCL_PIN GPIO_Pin_5
#define I2C_MASTER_SCL_NUM GPIO_NUM_5
#define I2C_MASTER_SCL_MODE GPIO_MODE_OUTPUT_OD
#define I2C_MASTER_SDA_PIN GPIO_Pin_4
#define I2C_MASTER_SDA_NUM GPIO_NUM_4
#define I2C_MASTER_SDA_MODE GPIO_MODE_OUTPUT_OD
#define I2C_MASTER_SCL_CLR() gpio_set_level(I2C_MASTER_SCL_NUM, 0)//i2c scl
#define I2C_MASTER_SCL_SET() gpio_set_level(I2C_MASTER_SCL_NUM, 1)
#define I2C_MASTER_SDA_CLR() gpio_set_level(I2C_MASTER_SDA_NUM, 0)//i2c sda
#define I2C_MASTER_SDA_SET() gpio_set_level(I2C_MASTER_SDA_NUM, 1)
//延时
void I2C_Master_delay(void)
{
uint8_t t = 1;
while (t--);
}
//SDA配置为输出
void I2C_Master_SDA_PIN_OUTPUT(void)
{
#if 0
gpio_config_t gpio_cfg = {
.pin_bit_mask = I2C_MASTER_SDA_PIN,
.mode = GPIO_MODE_OUTPUT_OD,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&gpio_cfg);
#else
gpio_set_direction(I2C_MASTER_SDA_NUM, GPIO_MODE_OUTPUT_OD);
#endif
}
//SDA配置为输入
void I2C_Master_SDA_PIN_INPUT(void)
{
#if 0
gpio_config_t gpio_cfg = {
.pin_bit_mask = I2C_MASTER_SDA_PIN,
.mode = GPIO_MODE_INPUT,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&gpio_cfg);
#else
gpio_set_direction(I2C_MASTER_SDA_NUM, GPIO_MODE_INPUT);
#endif
}
//读取SDA输入电平
uint8_t I2C_Master_SDA_PIN_GET(void)
{
return gpio_get_level(I2C_MASTER_SDA_NUM);
}
//起始信号
void I2C_Master_Start(void)
{
I2C_Master_SDA_PIN_OUTPUT();
I2C_MASTER_SDA_SET();
I2C_MASTER_SCL_SET();
I2C_Master_delay();
I2C_MASTER_SDA_CLR();
I2C_Master_delay();
I2C_MASTER_SCL_CLR();
I2C_Master_delay();
}
//结束信号
void I2C_Master_Stop(void)
{
I2C_Master_SDA_PIN_OUTPUT();
I2C_MASTER_SDA_CLR();
I2C_MASTER_SCL_SET();
I2C_Master_delay();
I2C_MASTER_SDA_SET();
}
//发送ack
void I2C_Master_Ack(void)
{
I2C_Master_SDA_PIN_OUTPUT();
I2C_MASTER_SDA_CLR();
I2C_MASTER_SCL_SET();
I2C_Master_delay();
I2C_MASTER_SCL_CLR();
I2C_Master_delay();
}
//发送nack
void I2C_Master_NAck(void)
{
I2C_Master_SDA_PIN_OUTPUT();
I2C_MASTER_SDA_SET();
I2C_MASTER_SCL_SET();
I2C_Master_delay();
I2C_MASTER_SCL_CLR();
I2C_Master_delay();
}
//等待信号响应
uint8_t I2C_Master_WaitAck(void) //测数据信号的电平
{
uint8_t ack;
I2C_Master_SDA_PIN_INPUT();
//I2C_MASTER_SDA_SET();
//I2C_Master_delay();
I2C_MASTER_SCL_SET();
ack = I2C_Master_SDA_PIN_GET();
I2C_Master_delay();
I2C_MASTER_SCL_CLR();
I2C_Master_delay();
return ack;
}
//写入一个字节
void I2C_Master_Send_Byte(uint8_t dat)
{
uint8_t i;
I2C_Master_SDA_PIN_OUTPUT();
for(i = 0; i < 8; i++)
{
if(dat & 0x80)//将dat的8位从最高位依次写入
{
I2C_MASTER_SDA_SET();
}
else
{
I2C_MASTER_SDA_CLR();
}
I2C_Master_delay();
I2C_MASTER_SCL_SET();
I2C_Master_delay();
I2C_MASTER_SCL_CLR();//将时钟信号设置为低电平
dat <<= 1;
}
}
//写入一个字节
void I2C_Master_Send_1Byte(uint8_t slave_addr, uint8_t reg_address, uint8_t dat)
{
I2C_Master_Start();
I2C_Master_Send_Byte(slave_addr);
I2C_Master_WaitAck();
I2C_Master_Send_Byte(reg_address);
I2C_Master_WaitAck();
I2C_Master_Send_Byte(dat);
I2C_Master_WaitAck();
I2C_Master_Stop();
}
//写入n个字节
void I2C_Master_Send_Bytes(uint8_t slave_addr, uint8_t reg_address, uint8_t *dat, uint16_t len)
{
uint16_t i = 0;
I2C_Master_Start();
I2C_Master_Send_Byte(slave_addr);
I2C_Master_WaitAck();
I2C_Master_Send_Byte(reg_address);
I2C_Master_WaitAck();
for (i = 0; i < len; i++)
{
I2C_Master_Send_Byte(dat[i++]);
I2C_Master_WaitAck();
}
I2C_Master_Stop();
}
//读一个字节
void I2C_Master_Read_Byte(uint8_t *dat)
{
uint8_t i;
*dat = 0;
I2C_Master_SDA_PIN_INPUT();
for(i = 0; i < 8; i++)
{
I2C_MASTER_SCL_SET();
I2C_Master_delay();
*dat <<= 1;
if (I2C_Master_SDA_PIN_GET())
*dat |= 0x01;
I2C_MASTER_SCL_CLR();
I2C_Master_delay();
}
}
void I2C_Master_Init(void)
{
gpio_config_t gpio_cfg;
gpio_cfg.pin_bit_mask = I2C_MASTER_SCL_PIN | I2C_MASTER_SDA_PIN;
gpio_cfg.mode = I2C_MASTER_SCL_MODE;
gpio_cfg.pull_up_en = 0;
gpio_cfg.pull_down_en = 0;
gpio_cfg.intr_type = GPIO_INTR_DISABLE;
gpio_config(&gpio_cfg);
I2C_MASTER_SCL_SET();
I2C_MASTER_SDA_SET();
}
#define FRAM_FM24CL04B 0 //4K bit / 512字节
#define FRAM_FM24CL16B 1 //16K bit / 2K字节
#define FRAM_FM24CLxx FRAM_FM24CL16B
#define FRAM_I2C_ADDR 0xA0
#if (FRAM_FM24CLxx == FRAM_FM24CL04B) //2pages x 256bytes
#define FRAM_PAGE_MAX 2
#define FRAM_PAGE_BYTES_MAX 256
#define FRAM_DATA_ADDR_END 0x1FF
#elif (FRAM_FM24CLxx == FRAM_FM24CL16B) //8pages x 256bytes
#define FRAM_PAGE_MAX 8
#define FRAM_PAGE_BYTES_MAX 256
#define FRAM_DATA_ADDR_END 0x7FF
#endif
/**
* @名称: FRAM_WriteBytes
* @描述: FM24CL04B 同一页写入多个字节 256字节x2页
* @参数:
* @参数:
* @日期:2023
* @返回: bool
**/
bool FRAM_WriteBytes(uint8_t *pBytes, uint32_t framStartAddress, uint32_t dataLenght)
{
uint8_t page;
if (framStartAddress > FRAM_DATA_ADDR_END) return false;
page = framStartAddress >> 8;
if ((page + 1) > FRAM_PAGE_MAX) return false;
I2C_Master_Start();
I2C_Master_Send_Byte(FRAM_I2C_ADDR | ((page << 1) & 0x0E) | 0x00);
I2C_Master_WaitAck();
I2C_Master_Send_Byte(0xFF & framStartAddress);
I2C_Master_WaitAck();
for (uint32_t i = 0; i < dataLenght; i++)
{
I2C_Master_Send_Byte(pBytes[i]);
I2C_Master_WaitAck();
}
I2C_Master_Stop();
return true;
}
/**
* @名称: FRAM_ReadBytes
* @描述: FM24CL04B 同一页读多个字节 256字节x2页
* @参数:
* @参数:
* @日期:2023
* @返回: bool
**/
bool FRAM_ReadBytes(uint8_t *pBytes, uint32_t framStartAddress, uint32_t dataLenght)
{
uint8_t page;
if (framStartAddress > FRAM_DATA_ADDR_END) return false;
page = framStartAddress >> 8;
if ((page + 1) > FRAM_PAGE_MAX) return false;
I2C_Master_Start();
I2C_Master_Send_Byte(FRAM_I2C_ADDR | ((page << 1) & 0x0E) | 0x00);
I2C_Master_WaitAck();
I2C_Master_Send_Byte(framStartAddress & 0xFF);
I2C_Master_WaitAck();
I2C_Master_Start();
I2C_Master_Send_Byte(FRAM_I2C_ADDR | ((page << 1) & 0x0E) | 0x01);
I2C_Master_WaitAck();
do
{
I2C_Master_Read_Byte(pBytes);
pBytes++;
if (dataLenght == 1) {
I2C_Master_NAck();
}
else {
I2C_Master_Ack();
}
}while(--dataLenght);
I2C_Master_Stop();
return true;
}
读写示例:
uint8_t datArray[10];
FRAM_WriteBytes(datArray, 0, sizeof(datArray)); //写page0第0-9
FRAM_WriteBytes(datArray, 256, sizeof(datArray)); //写page1第0-9
FRAM_WriteBytes(datArray, 512, sizeof(datArray)); //写page2第0-9
FRAM_ReadBytes(datArray, 0, sizeof(datArray)); //读page0第0-9
FRAM_ReadBytes(datArray, 256, sizeof(datArray)); //读page1第0-9
FRAM_ReadBytes(datArray, 512, sizeof(datArray)); //读page2第0-9
三、I2C驱动 (硬 – STM32F103)
@ CSDN Tyrion.Mon
/**
* @名称: FRAM_Init
* @描述: FRAM IO初始化 i2c配置
* @参数:
* @参数:
* @日期:2019
* @返回: bool
**/
bool FRAM_Init(void)
{
I2C_InitTypeDef I2C_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(FRAM_GPIO_CLK,ENABLE);
RCC_APB1PeriphClockCmd(FRAM_I2C_CLK, ENABLE);
I2C_InitStruct.I2C_ClockSpeed = 100000;
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; //该参数只有在 I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义。
I2C_InitStruct.I2C_OwnAddress1 = 0x75;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitStruct);
I2C_Cmd(FRAM_I2C, ENABLE);
GPIO_InitStruct.GPIO_Pin = FRAM_SCL_PIN | FRAM_SDA_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(FRAM_GPIO, &GPIO_InitStruct);
GPIO_SetBits(FRAM_GPIO, FRAM_SCL_PIN);
GPIO_SetBits(FRAM_GPIO, FRAM_SDA_PIN);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; //先把GPIO设为推挽输出并拉倒高电平再转设为复用开漏输出,这样可以有效解决I2C设备死锁的问题???
GPIO_Init(FRAM_GPIO, &GPIO_InitStruct);
return true;
}
/**
* @名称: FRAM_WriteBytes
* @描述: FM24CL04B 同一页写入多个字节 256字节x2页
* @参数:
* @参数:
* @日期:2019
* @返回: bool
**/
bool FRAM_WriteBytes(uint8_t framPage, uint8_t *pBytes, uint8_t framStartAddress, uint8_t dataLenght)
{
if(framPage > 1 || !dataLenght) return false;
if(framStartAddress + dataLenght > 256) return false;
/* Send START condition */
I2C_GenerateSTART(FRAM_I2C, ENABLE);
/* Test on EV5 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
//EV5事件被检测到,发送设备地址
I2C_Send7bitAddress(FRAM_I2C, 0xA0 | (FRAM_ADDRESS << 2) | (framPage << 1), I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);
//EV6事件被检测到,发送要操作的存储单元地址
I2C_SendData (FRAM_I2C, framStartAddress);
/* Test on EV8 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);
do
{
//EV8事件被检测到,发送要存储的数据
I2C_SendData (FRAM_I2C, *pBytes++);
/* Test on EV8 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);
}while(--dataLenght);
/* Send STOP condition */
I2C_GenerateSTOP(FRAM_I2C, ENABLE);
return true;
}
/**
* @名称: FRAM_ReadBytes
* @描述: FM24CL04B 同一页读多个字节 256字节x2页
* @参数:
* @参数:
* @返回: bool
**/
bool FRAM_ReadBytes(uint8_t framPage, uint8_t *pBytes, uint8_t framStartAddress, uint8_t dataLenght)
{
if(framPage > 1 || !dataLenght) return false;
if(framStartAddress + dataLenght > 256) return false;
/* Send START condition */
I2C_GenerateSTART(FRAM_I2C, ENABLE);
/* Test on EV5 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
//EV5事件被检测到,发送设备地址
I2C_Send7bitAddress(FRAM_I2C, 0xA0 | (FRAM_ADDRESS << 2) | (framPage << 1), I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);
//EV6事件被检测到,发送要操作的存储单元地址
I2C_SendData (FRAM_I2C, framStartAddress);
/* Test on EV8 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);
/* Send START condition */
I2C_GenerateSTART(FRAM_I2C, ENABLE);
/* Test on EV5 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
//EV5事件被检测到,发送设备地址
I2C_Send7bitAddress(FRAM_I2C, 0xA0 | (FRAM_ADDRESS << 2) | (framPage << 1), I2C_Direction_Receiver);
/* Test on EV6 and clear it */
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) == ERROR);
do
{
if(dataLenght == 1)
{
I2C_AcknowledgeConfig (FRAM_I2C, DISABLE);
}
//EV7事件被检测到
while(I2C_CheckEvent(FRAM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR);
//EV8事件被检测到,发送要存储的数据
*pBytes++ = I2C_ReceiveData (FRAM_I2C);
}while(--dataLenght);
/* Send STOP condition */
I2C_GenerateSTOP(FRAM_I2C, ENABLE);
I2C_AcknowledgeConfig (FRAM_I2C, ENABLE);
return true;
}
读写示例:
uint8_t FRAM_ENERGY_BANK[10];
FRAM_ReadBytes(0, FRAM_ENERGY_BANK, 0x00, 10); //读page0第0-9个数据
FRAM_ReadBytes(1, FRAM_ENERGY_BANK, 0x00, 10); //读page1第0-9个数据
FRAM_WriteBytes(0, FRAM_ENERGY_BANK, 0x00, 10); //写page0第0-9个数据
FRAM_WriteBytes(1, FRAM_ENERGY_BANK, 0x00, 10); //写page1第0-9个数据