目录
一、简单介绍
EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。
笔者所用模块为AT24C256,存储容量256Kb即32KB(32K字节)。模块的原理图如下
引脚名称 | 功能 |
A0-A1(A2其实是NC脚,原理图画得不对) | 器件地址输入 |
SDA(原理图中的DATA脚) | 串行数据 |
SCL | 时钟 |
WP(原理图中的HOLD脚) | 写保护(高电平有效) |
VCC | 接高电平(1.8-5.5V宽电压) |
GND | 地 |
驱动协议是IIC,考虑开启硬件IIC进行通信,笔者选择使用IIC2
二、配置工程
打开CubeMX,配置时钟,调试接口,工程名,目录等
配置iic
配置串口用于显示信息
三、硬件连接
如上一步的配置,将模块与单片机如下连接
PB10---->SCL
PB11---->SDA
VCC---->3V3
GND---->GND
四、代码编写
一、随机写入一个字节
先发送器件地址或上读写指令,再发送要写入的地址(占2个字节),再发送写入的字节,再发送停止位。
由于A0和A1均为低电平,即0,那么器件地址为0x50(0101 0000)
此时为写入,R/W就是0,经过组合后为0xA0
测试代码
即向AT24C256内存地址为0的地方写入数据“1”,需要注意的是AT24C256写入数据需要等待5~10ms,要加一个延时
波形如下
代码编写
void AT24C256_WriteByte(uint16_t addr,uint8_t dat)
{
HAL_I2C_Mem_Write(&hi2c2,AT24C256_ADDR_WRITE,addr,AT24C256_ADDR_LEN,&dat,1,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT);
}
二、连续写入
连续写入和随机写入差不多,只是如果一次写的个数超过一页的字节数或者地址计到一页的尾部时,会重新回到这页开头覆盖写过的数而不会自动翻页,这是需要处理的地方。
代码如下
/*
AT24C256有256K个位,即32KB,32k字节,寻址空间为0~0x7FFF,
分为两个字节,地址1为0~0x7F,地址AT24C256_ADDR_LEN为0~0xFF
一页有64个字节,分为512页
*/
void AT24C256_WriteMultiByte(uint16_t addr,uint8_t* dat,uint16_t n)
{
uint16_t i = 0;
uint16_t cnt = 0; //写入字节计数
uint16_t head;
uint16_t left;
uint16_t tail;
if((n + addr - 1)/AT24C256_PAGE_SIZE == addr/AT24C256_PAGE_SIZE) //如果在同一页
{
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE, addr, AT24C256_ADDR_LEN, dat, n, 0xFFFF);
HAL_Delay(AT24C256_WAIT_TIME_UINT*n);
}
else
{
head = (addr / AT24C256_PAGE_SIZE+1) * AT24C256_PAGE_SIZE - addr; //开始页剩余待写入字节数
left = n - head; //除去开始页剩下字节数
tail=left-left/AT24C256_PAGE_SIZE*AT24C256_PAGE_SIZE; //末页待写入字节数
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE,addr,AT24C256_ADDR_LEN,dat,head,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT*head);
for(i=0;i<left/AT24C256_PAGE_SIZE;i++)
{
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE, addr+head+i*AT24C256_PAGE_SIZE,\
AT24C256_ADDR_LEN, dat+head+i*AT24C256_PAGE_SIZE,AT24C256_PAGE_SIZE, 0xFFFF);
HAL_Delay(AT24C256_WAIT_TIME_UINT*AT24C256_PAGE_SIZE);
}
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE,addr+head+i*AT24C256_PAGE_SIZE,AT24C256_ADDR_LEN,\
dat+head+i*AT24C256_PAGE_SIZE,tail,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT*tail);
}
}
三、随机读取
要先发送一个假写的动作,把地址定位到要读的地方,然后再发送读指令,此时模块就会把数据一个接一个地clock出来了
测试代码
波形如下
代码编写
uint8_t AT24C256_ReadByte(uint16_t addr)
{
uint8_t p;
HAL_I2C_Mem_Read(&hi2c2,AT24C256_ADDR_READ,addr,AT24C256_ADDR_LEN,&p,1,0xffff);
return p;
}
四、连续读取
由于连续读写的时候,地址会自动增加,并且会翻页,所以不必担心重复读取的问题,只要别读比最大地址范围还大的地址一般没有问题(经过笔者测试,如果读取0x8000地址的值,得到的是0x0000处的值),而且读数据不用等待,非常不错
代码如下
/*
连续读字节,参数为开始读地址,用来存储数据的地址,要读取的个数
*/
void AT24C256_ReadMultiByte(uint16_t addr,uint8_t* dat,uint16_t n)
{
if(addr+n<0x8000)
HAL_I2C_Mem_Read(&hi2c2,AT24C256_ADDR_READ,addr,AT24C256_ADDR_LEN,dat,n,0xffff);
}
五、效果展示
主函数main.c主要代码如下
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "AT24C256.h"
#include "stdio.h"
#include "stdlib.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TEST_BUFFER_LEN 1024
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint16_t x;
uint8_t buffer[TEST_BUFFER_LEN];
uint8_t b[TEST_BUFFER_LEN];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_I2C2_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
for(x=0;x<TEST_BUFFER_LEN;x++)
{
*(buffer+x)=x%256;
}
printf("start\r\n");
AT24C256_WriteMultiByte(0x0,buffer,TEST_BUFFER_LEN);
printf("end\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
AT24C256_ReadMultiByte(0,b,TEST_BUFFER_LEN);
for(uint16_t i=0;i<TEST_BUFFER_LEN;i++)
{
x=AT24C256_ReadByte(i);
printf("addr:%d\t-->:%d\r\n",i,b[i]);
}
printf("\r\n\r\n\r\n");
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、驱动附录
AT24C256.h
#ifndef AT24C256_H
#define AT24C256_H
#include "main.h"
#define AT24C256_ADDR_LEN 2
#define AT24C256_ADDR_WRITE 0xA0
#define AT24C256_ADDR_READ 0xA1
#define AT24C256_PAGE_SIZE 64
#define AT24C256_WAIT_TIME_UINT 2
#define AT24C256_MEM_LEN 0x8000
void AT24C256_WriteByte(uint16_t add,uint8_t dat);
uint8_t AT24C256_ReadByte(uint16_t add);
void AT24C256_WriteMultiByte(uint16_t add,uint8_t* dat,uint16_t n);
void AT24C256_Fill(uint8_t fill);
void AT24C256_ReadMultiByte(uint16_t addr,uint8_t* dat,uint16_t n);
#endif
AT24C256.c
#include "i2c.h"
#include "AT24C256.h"
#include "string.h"
uint8_t erase[512];
void AT24C256_WriteByte(uint16_t addr,uint8_t dat)
{
HAL_I2C_Mem_Write(&hi2c2,AT24C256_ADDR_WRITE,addr,AT24C256_ADDR_LEN,&dat,1,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT);
}
/*
AT24C256有256K个位,即32KB,32k字节,寻址空间为0~0x7FFF,
分为两个字节,地址1为0~0x7F,地址2为0~0xFF
一页有64个字节,分为512页
*/
void AT24C256_WriteMultiByte(uint16_t addr,uint8_t* dat,uint16_t n)
{
uint16_t i = 0;
uint16_t cnt = 0; //写入字节计数
uint16_t head;
uint16_t left;
uint16_t tail;
if((n + addr - 1)/AT24C256_PAGE_SIZE == addr/AT24C256_PAGE_SIZE) //如果在同一页
{
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE, addr, AT24C256_ADDR_LEN, dat, n, 0xFFFF);
HAL_Delay(AT24C256_WAIT_TIME_UINT*n);
}
else
{
head = (addr / AT24C256_PAGE_SIZE+1) * AT24C256_PAGE_SIZE - addr; //开始页剩余待写入字节数
left = n - head; //除去开始页剩下字节数
tail=left-left/AT24C256_PAGE_SIZE*AT24C256_PAGE_SIZE; //末页待写入字节数
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE,addr,AT24C256_ADDR_LEN,dat,head,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT*head);
for(i=0;i<left/AT24C256_PAGE_SIZE;i++)
{
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE, addr+head+i*AT24C256_PAGE_SIZE,\
AT24C256_ADDR_LEN, dat+head+i*AT24C256_PAGE_SIZE,AT24C256_PAGE_SIZE, 0xFFFF);
HAL_Delay(AT24C256_WAIT_TIME_UINT*AT24C256_PAGE_SIZE);
}
HAL_I2C_Mem_Write(&hi2c2, AT24C256_ADDR_WRITE,addr+head+i*AT24C256_PAGE_SIZE,AT24C256_ADDR_LEN,\
dat+head+i*AT24C256_PAGE_SIZE,tail,0xffff);
HAL_Delay(AT24C256_WAIT_TIME_UINT*tail);
}
}
uint8_t AT24C256_ReadByte(uint16_t addr)
{
uint8_t p;
HAL_I2C_Mem_Read(&hi2c2,AT24C256_ADDR_READ,addr,AT24C256_ADDR_LEN,&p,1,0xffff);
return p;
}
/*
连续读字节,参数为开始读地址,用来存储数据的地址,要读取的个数
*/
void AT24C256_ReadMultiByte(uint16_t addr,uint8_t* dat,uint16_t n)
{
if(addr+n<0x8000)
HAL_I2C_Mem_Read(&hi2c2,AT24C256_ADDR_READ,addr,AT24C256_ADDR_LEN,dat,n,0xffff);
}
void AT24C256_Fill(uint8_t fill)
{
memset(erase,fill,512);
for(uint16_t i=0;i<64;i++)
{
AT24C256_WriteMultiByte(i*512,erase,512);
}
}