打印
[应用相关]

EEPROM芯片24C02的基础知识及其驱动程序设计

[复制链接]
26|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
磨砂|  楼主 | 2025-1-11 19:01 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
文章参照使用旺宝红龙开发板,STM32F407ZGT6 KIT V1.0。

一、接口和通信协议
        开发板上有一个I2C接口的EEPROM芯片AT24C02,是ATMEL公司的产品。因为其他一些厂家的芯片与AT24C02引脚和功能完全兼容,一般就统称为24C02。24C02存储容量为256字节,它的电路连接如图所示。其SDA和SCL引脚连接STM32F407芯片的I2C1接口,使用引脚PF0和PF9。WP是写保护引脚,WP接地时,对24C02芯片可读可写。



1、设备地址
        24C02的I2C设备地址组成如图所示。A2、A1、A0位由芯片的引脚A2、A1、A0的电平决定,原理图中这3个引脚都接地,所以都是0。最低位R/W是读写标志位,当主设备对24C02进行写操作时,R/W为0,进行读操作时,R/W为1。所以,24C02的写操作地址为0xA0(10100000),读操作地址为0xA1(10100001)。HAL驱动程序函数中需要传递I2C从设备的地址时,都使用写操作地址。



2、写1字节数据
        24C02的读写操作比较简单,存储空间可反复读写。

        MCU向24C02写入1字节数据的SDA传输内容和顺序如下图。

        操作的顺序如下:

●主机发送起始信号,然后发送器件的写操作地址。

● 24C02应答ACK后,主机再发送8位字地址,这是24C02内部存储单元的地址。8位地址的范围是0~255,也就是24C02内256字节存储单元的地址。

● 24C02应答ACK后,主机再发送需要写入的1字节的数据。

●从机接收完数据后,应答ACK,主机发停止信号结束传输。



3、连续写多字节数据
        24C02内部存储区域按页划分,每页8字节,所以256字节的存储单元分为32页,页的起始地址是8×N,其中,N=0,1,…,31。

        可以在一次I2C通信过程(一个起始信号与一个停止信号限定的通信过程)中向24C02连续写入多个字节的数据,SDA传输内容和顺序如下图。图中的n是数据存储的起始地址,存储的数据字节数为1+x。24C02会自动将接收的数据从指定的起始地址开始存储,但是要注意,连续写入的数据的存储位置不能超过页的边界,否则,将自动从这页的开始位置继续存储。



        所以,在连续写数据时,如果数据起始地址在页的起始位置,则一次最多可写8字节的数据。当然,数据存储起始地址也可以不在页的起始位置,这时要注意,一次写入的数据不要超过页的边界。

4、读1字节数据
        可以从24C02的任何一个存储位置读取1字节的数据,读取1字节数据时,SDA传输的内容和顺序如下图。主设备先进行一次写操作,写入需要读取的存储单元的地址,然后再进行一次读操作,读取的1字节数据就是所指定的存储地址的存储内容。



5、连续读多字节数据
        可以从24C02一次性连续读取多字节的数据,且读取数据时不受页边界的影响,也就是读取数据的长度可以超过8字节。连续读多个字节数据的SDA传输内容和顺序如下图。主设备先进行一次写操作,写入需要读取的存储单元的地址,然后再进行一次读操作,连续读取多字节,存储器内部将自动移动存储位置,且存储位置不受页边界的影响。



        在使用I2C的HAL驱动函数进行24C02的数据读写时,以上各图描述的传输时序是由I2C硬件接口完成的,工程师不需要理睬这些时序。

二、驱动程序设计
        使用I2C接口的HAL驱动函数,根据24C02的数据读写操作的定义,编写24C02的驱动程序,将24C02的一些常用操作封装为函数,以便其他项目调用。

        24C02的驱动程序文件包括头文件24cxx.h和源程序文件24cxx.c,头文件24cxx.h是24C02驱动程序的接口定义,其完整代码如下:

1、24cxx.h

/*
*  文件名:24cxx.h
*         功能描述: 24C02 EEPROM驱动程序,使用HAL库
*
*         (1)24C02是 256字节EEPROM,可以单个字节读写,连续读写时按页读写,每页8字节。所以,按页读写时最多8字节,页号0--31
*
*         (2)连续写入数据时要注意不要超过页的边界,否则从页的开始处重新写,会覆盖原来的内容。
*
*         (3)24C02地址是 0b1010xxxy, 其中xxx由芯片的地址引脚A2,A1,A0决定,一般接地,所以是0xA0, HAL库在读写时自动在最后一位写0或1进行读或写操作
*
*         (4)24C02的地址数据长度是8位,使用宏定义符号I2C_MEMADD_SIZE_8BIT
*/


#ifndef __24cxx_H
#define __24cxx_H

#include "stm32f4xx_hal.h"
#include "i2c.h"                                 //i2c.h中定义了hi2c2
/* 两个与硬件相关的定义,   */
#define I2C_HANDLE hi2c2                //I2C外设对象变量,利用i2c.h中定义的hi2c2
#define        DEV_ADDR_24CXX 0x00A0        //24C02的写地址
// EEPROM存储器参数
#define        PAGE_SIZE_24CXX 0x0008        //24C02的Page大小为8字节
#define        MEM_SIZE_24CXX (uint16_t)256 //24C02总共容量字节数,8*32=256字节

//检查设备是否准备好
HAL_StatusTypeDef EP24C_IsDeviceReady(void);

//在任意地址写入一个字节
HAL_StatusTypeDef EP24C_WriteOneByte(uint16_t memAddress, uint8_t byteData);

//在任意地址读出一个字节
HAL_StatusTypeDef EP24C_ReadOneByte(uint16_t memAddress, uint8_t *byteData);

//连续读取数据,任意地址,任意长度,不受页的限制
HAL_StatusTypeDef EP24C_ReadBytes(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);

/*限定在一个页内写入连续数据,最多8字节。任意起始地址,
*但是起始地址+数据长度不能超过页边界,即不能超过地址8*N-1 */
HAL_StatusTypeDef EP24C_WriteInOnePage(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);

//写任意长的数据,可以超过8字节,但数据地址必须从页首开始,即 8*N
HAL_StatusTypeDef EP24C_WriteLongData(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);

#endif

        为便于程序的移植,在文件24cxx.h中定义一个宏I2C_HANDLE替代hi2c2。如果使用了不同的I2C接口,只需修改这个宏定义即可,而I2C接口的外设初始化由CubeMX自动生成的函数完成。在24cxx.h中还定义了表示24C02地址的宏DEV_ADDR_24CXX,如果实际电路中的24C02的I2C地址被修改了,修改这个宏即可。

        在文件24cxx.h中定义了5个函数,前面4个都是直接封装I2C的HAL传输函数实现的,最后1个函数EP24C_WriteLongData()能自动将一个长的数据拆分为多个页(每页8字节)写入,这5个函数都使用I2C的阻塞式存储器数据传输函数。

2、24cxx.c

/* 文件: 24cxx.c
* 描述: 24C02驱动程序源程序文件
*/

#include "24cxx.h"
#define        EP24C_TIMEOUT 200                                                //超时等待时间,单位:ms
#define        EP24C_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT  //存储器地址大小,8位地址

//检查设备是否准备好I2C通讯,返回HAL_OK 表示OK
HAL_StatusTypeDef EP24C_IsDeviceReady(void)
{
        uint32_t Trials=10;                //尝试次数
        HAL_StatusTypeDef result=HAL_I2C_IsDeviceReady(&I2C_HANDLE,DEV_ADDR_24CXX,Trials,EP24C_TIMEOUT);
        return result;
}

//向任意地址写入一个字节的数据, memAddr是存储器内部地址,byteData是需要写入的1个字节的数据
HAL_StatusTypeDef EP24C_WriteOneByte(uint16_t memAddress,uint8_t byteData)
{
        HAL_StatusTypeDef result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                        EP24C_MEMADD_SIZE,&byteData,1,EP24C_TIMEOUT);
        return        result;
}

//从任意地址读出一个字节的数据, memAddr是存储器内部地址,byteData是读出的1个字节的数据,若返回HAL_OK表示读取成功
HAL_StatusTypeDef EP24C_ReadOneByte(uint16_t memAddress,uint8_t *byteData)
{
        HAL_StatusTypeDef result=HAL_I2C_Mem_Read(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                        EP24C_MEMADD_SIZE,byteData,1,EP24C_TIMEOUT);
        return result;
}

//连续读取数据,任意地址,任意长度,不受页的限制
HAL_StatusTypeDef EP24C_ReadBytes(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{
        if(bufferLen>MEM_SIZE_24CXX)        //超过总存储容量
                return HAL_ERROR;

        HAL_StatusTypeDef result=HAL_I2C_Mem_Read(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                        EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);
        return result;
}

//限定在一个页内写入连续数据,最多8字节。任意起始地址,但是起始地址+数据长度不能超过页边界
HAL_StatusTypeDef EP24C_WriteInOnePage(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{
        if(bufferLen>PAGE_SIZE_24CXX)        //数据长度不能大于页的大小
                return HAL_ERROR;

        HAL_StatusTypeDef result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                        EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);
        return result;
}

//写任意长的数据,可以超过8字节,但数据地址必须从页首开始,即 8*N。自动分解为多次写入
HAL_StatusTypeDef EP24C_WriteLongData(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{
        if(bufferLen>MEM_SIZE_24CXX)        //超过总存储容量
                return HAL_ERROR;

        HAL_StatusTypeDef result=HAL_ERROR;
        if(bufferLen<=PAGE_SIZE_24CXX)        //不超过1个page,直接写入后退出
        {
                result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                                EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);
                return result;
        }

        uint8_t *pt=pBuffer;                        //临时指针,不能改变传入的指针
        uint16_t pageCount=bufferLen/PAGE_SIZE_24CXX;        //Page个数
        for(uint16_t i=0; i<pageCount; i++)        //一次写入一个page的数据
        {
                result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                                EP24C_MEMADD_SIZE,pt,PAGE_SIZE_24CXX,EP24C_TIMEOUT);
                pt += PAGE_SIZE_24CXX;
                memAddress += PAGE_SIZE_24CXX;
                HAL_Delay(5);                                        //必须有延时,以等待页写完
                if (result != HAL_OK)
                        return result;
        }

        uint16_t leftBytes=bufferLen % PAGE_SIZE_24CXX;  //余数
        if (leftBytes>0)                                        //写入剩余的数据
                result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,
                                EP24C_MEMADD_SIZE,pt,leftBytes,EP24C_TIMEOUT);
        return result;
}


        24C02是I2C接口的存储器,使用I2C的HAL驱动程序中的存储器数据传输函数进行数据读写更方便。这几个函数就是封装了函数HAL_I2C_Mem_Write()和HAL_I2C_Mem_Read(),只是函数的接口定义更简化,便于用户程序调用。

函数EP24C_WriteOneByte()用于在任意地址写入1字节数据。
函数EP24C_ReadOneByte()用于从任意地址读取1字节数据。
函数EP24C_ReadBytes()用于从任意地址开始读取任意长度的数据,不受每页8字节的限制。
函数EP24C_WriteInOnePage()限定在一个页内写数据,可以是任意起始地址,但一次最多写入8字节。
函数EP24C_WriteLongData()用于从一个页的起始地址开始写入超过8字节的数据,函数内部会将数据拆分为多个页,分为多次写入。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/wenchm/article/details/144736520

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

96

主题

4179

帖子

2

粉丝