EEPROM读写实验

更多交流欢迎关注作者抖音号:81849645041

目标

实现从机设备EEPROM读写数据。

原理

EEPROM 是一种掉电后数据不丢失的存储器,常用来存储一些配置信息,以便系统重新上电的时候加载。EEPROM 芯片最常用的通讯方式就是 I2C 协议。

STM32F4 开发板板载的 EEPROM 芯片型号为 24C02。该芯片的总容量是 256 个字节,该芯片通过 IIC 总线与外部连接。

EEPROM 硬件连接图

        本实验板中的 EEPROM 芯片(型号:AT24C02)的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C 引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。 EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,图中的 R/W 是读写方向位,与地址无关。

 

        按照我们此处的连接,A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是:1010 000b ,即 0x50。由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1 时,表示读方向,加上 7 位地址,其值为“0xA1”,常称该值为“读地址”。

        EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,此处直接接地,不使用写保护功能。

准备

        MDK5 开发环境。

        STM32F4xx HAL库。

        STM32F407 开发板。

        STM32F4xx 参考手册。

        STM32F4xx 数据手册。

        STM32F407 开发板电路原理图。

步骤

  • 使用IIC通讯实验实现EEPROM的读写,头文件中添加宏和函数。
#ifndef __BSP_HARD_IIC_H
#define __BSP_HARD_IIC_H

#include "stm32f4xx.h"

#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t

// I2C时钟
#define I2C_Speed 400000
//  STM32 自身的 I2C 地址,这个地址只要与 STM32 外挂的 I2C 器件地址不一样即可
#define I2C_OWN_ADDRESS7 0X0A

#define ADDR_24LCxx_Write 0xA0 // EEPROM 设备地址 写方向
#define ADDR_24LCxx_Read 0xA1 // EEPROM 设备地址 读方向

void IIC_MODE_Init(void); // I2C 模式初始化
u8 I2C_EE_IsDeviceReady(u8 DevAddress); // 检测EEPROM设备是否就绪

u8 I2C_EE_ByteWrite(u8* TxData,u8 DevAddress,u8 WriteAddr); // 写一个字节
u8 I2C_EE_ByteRead(u8* RxData,u8 DevAddress,u8 WriteAddr); // 读一个字节

u8 I2C_EE_NByteWrite(u8* TxData,u8 DevAddress,u8 WriteAddr,u8 DataSize); // 写多个字节
u8 I2C_EE_NByteRead(u8* RxData,u8 DevAddress,u8 ReadAddr,u8 DataSize); // 读多个字节
#endif
  • 实现EEPROM读写函数。单字节的读写和多字节的读写。主要是调用HAL库函数HAL_I2C_Mem_Write()写数据,HAL_I2C_Mem_Read() 读数据。

        HAL_I2C_Mem_Write()函数解析:

HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
/**
  * @brief  以阻塞模式将大量数据写入特定的内存地址
  * @param  hi2c: I2C_HandleTypeDef 句柄
  * @param  DevAddress: 目标设备地址
  * @param  MemAddress: 内存地址
  * @param  MemAddSize: 内存地址的大小
  * @param  pData: 数据缓冲区
  * @param  Size: 连接次数
  * @param  Timeout: 超时时间
  * @retval HAL status
  */

        HAL_I2C_Mem_Read()

HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
/**
  * @brief  从特定的内存地址以阻塞模式读取大量数据
  * @param  hi2c: I2C_HandleTypeDef 句柄
  * @param  DevAddress: 目标设备地址
  * @param  MemAddress: 内存地址
  * @param  MemAddSize: 内存地址的大小
  * @param  pData: 数据缓冲区
  * @param  Size: 连接次数
  * @param  Timeout: 超时时间
  * @retval HAL status
  */

        完整代码如下:

#include "bsp_hard_i2c.h"

I2C_HandleTypeDef I2C1_Handle;

void IIC_MODE_Init(void)
{	
	I2C1_Handle.Instance = I2C1; // I2C1
	I2C1_Handle.Init.DutyCycle = I2C_DUTYCYCLE_2; //指定时钟占空比,可选 low/high = 2:1 及 16:9 模式
	I2C1_Handle.Init.OwnAddress1 = I2C_OWN_ADDRESS7; //指定地址
	I2C1_Handle.Init.OwnAddress2 = 0; //指定地址
	I2C1_Handle.Init.ClockSpeed = I2C_Speed; // 设置 SCL 时钟频率,此值要低于 40 0000
	I2C1_Handle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;//指定地址的长度,可为 7 位及 10 位
	I2C1_Handle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 指定是否选择双寻址模式
	I2C1_Handle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 指定是否选择通用调用模式
	I2C1_Handle.Init.NoStretchMode  = I2C_NOSTRETCH_DISABLE; // 指定是否选择nostretch模式
	HAL_I2C_Init(&I2C1_Handle); // 初始化
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) //HAL_I2C_Init() 回调函数
{
	GPIO_InitTypeDef I2C1_GPIO_Init;
	if(hi2c->Instance == I2C1){
	__HAL_RCC_I2C1_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	
	I2C1_GPIO_Init.Pin = GPIO_PIN_8 | GPIO_PIN_9;
	I2C1_GPIO_Init.Mode = GPIO_MODE_AF_OD; //开漏
	I2C1_GPIO_Init.Pull = GPIO_PULLUP; // 上拉
	I2C1_GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH;
	I2C1_GPIO_Init.Alternate = GPIO_AF4_I2C1; // 复用为I2c
	HAL_GPIO_Init(GPIOB,&I2C1_GPIO_Init);
	}
}

/**HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout)
  * @brief  检查目标设备是否准备好通信.
  * @note   此函数用于内存设备
  * @param  hi2c: I2C_HandleTypeDef 
  * @param  DevAddress: 目标设备地址
  * @param  Trials: 次数
  * @param  Timeout: 超时时间
  * @retval HAL status
  */
u8 I2C_EE_IsDeviceReady(u8 DevAddress)
{
	// HAL_I2C_IsDeviceReady()检测设备是否就绪
	if(HAL_I2C_IsDeviceReady(&I2C1_Handle,DevAddress,1,0xFF) != HAL_OK){
		return 0;
	}
	return 1;
}

// 写单个字节
u8 I2C_EE_ByteWrite(u8* TxData,u8 DevAddress,u8 WriteAddr)
{
	if(HAL_I2C_Mem_Write(&I2C1_Handle,DevAddress,WriteAddr,I2C_MEMADD_SIZE_8BIT,TxData,1,0xFF) != HAL_OK){
		return 0;
	}
	return 1;
}

// 读单个字节
u8 I2C_EE_ByteRead(u8* RxData,u8 DevAddress,u8 ReadAddr)
{
	if(HAL_I2C_Mem_Read(&I2C1_Handle,DevAddress,ReadAddr,I2C_MEMADD_SIZE_8BIT,RxData,1,0xFF) != HAL_OK){
		return 0;
	}
	return 1;
}

// 写多个字节
u8 I2C_EE_NByteWrite(u8* TxData,u8 DevAddress,u8 WriteAddr,u8 DataSize) 
{
	if(HAL_I2C_Mem_Write(&I2C1_Handle,DevAddress,WriteAddr,I2C_MEMADD_SIZE_8BIT,TxData,DataSize,0xFF) != HAL_OK){
		return 0;
	}
	return 1;
}

// 读多个字节
u8 I2C_EE_NByteRead(u8* RxData,u8 DevAddress,u8 ReadAddr,u8 DataSize) 
{
	if(HAL_I2C_Mem_Read(&I2C1_Handle,DevAddress,ReadAddr,I2C_MEMADD_SIZE_8BIT,RxData,DataSize,0xFF) != HAL_OK){
		return 0;
	}
	return 1;
}
  • main.c中程序如下:

        第一步:初始化IIC。

        第二步:判断24C02设备就绪,并在内存地址0x10写入单个字节数据0x55。内存地址0x50写入多个数据。

        第三步:按下KEY2,读取单个字节。按下KEY1读取多个字节。

#include "bsp_clock.h"
#include "bsp_uart.h"
#include "bsp_key.h"
#include "bsp_led.h"
#include "bsp_soft_i2c.h"
#include "bsp_hard_i2c.h"
#include <string.h> 

int main(void)
{
	u8 tx = 0x55;
	u8 rx = 0;
	u8 txBuf[5] = {1,2,3,4,5};
	u8 rxBuf[5];
	
	CLOCLK_Init(); // 初始化系统时钟
	UART_Init(); // 串口初始化
	KEY_Init(); // 按键初始化
	LED_Init(); // LED初始化
	IIC_MODE_Init();// iic初始化
	
	// 从机设备就绪
	if(I2C_EE_IsDeviceReady(ADDR_24LCxx_Write)){	
		// 写入一个字节
		I2C_EE_ByteWrite(&tx,ADDR_24LCxx_Write,0x10);
		HAL_Delay(5);
		// 写入多个字节
		I2C_EE_NByteWrite(txBuf,ADDR_24LCxx_Write,0x50,sizeof(txBuf));
	}  
	
	while(1)
	{
		if(KEY_Scan(0) == 1){ // KEY2 读取单字节
			// 读取单字节
			I2C_EE_ByteRead(&rx,ADDR_24LCxx_Read,0x10);
			// 读取正确 打印数据
			if(rx == tx){
				printf("Read Byte: \n");
				printf("tx: 0x%x  rx: 0x%x \n",tx,rx);
			}
			else{
				printf("Read Byte Error! \n");
			}
		}
		
		if(KEY_Scan(0) == 2){ // KEY1 读取多字节
			// 读取多字节
			I2C_EE_NByteRead(rxBuf,ADDR_24LCxx_Read,0x50,sizeof(rxBuf));
			// 读取正确 打印数据
			if(memcmp(txBuf,rxBuf,sizeof(rxBuf)) == 0){
				u8 i;
				printf("Read Buf: \n");
				for(i=0;i<5;i++){
					printf("%d ",rxBuf[i]);
				}
			}
			else{
				printf("Read Buf Error!");
			}
		}
	}
}

现象

        将程序下载到开发板中,如果检测设备存在就绪,就会在0x10写入单个数据,0x50写入多个数据。

        按下KEY2,读取带个字节,如果和写入的相同,就串口打印。

         按下KEY1,读取多个字节数据,如果和写入的相同,就打印读取的数据。

  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奚海蛟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值