首页 > 其他分享 >14. I2C读取EEPROM

14. I2C读取EEPROM

时间:2024-03-15 21:44:19浏览次数:38  
标签:字节 应答 param 地址 AT24C02 I2C EEPROM 14

一、AT24C02简介

  AT24C02 是一个 2K bit 的串行 EEPROM 存储器,内部含有 256 个字节。在 24C02 里面还有一个 8 字节的页写缓冲器。该设备的通信方式 I2C,通过其 SCL 和 SDA 与其他设备通信,芯片的引脚图如下图所示。

AT24C02引脚图

  上图中有一个 WP,这个是写保护引脚,接高电平只读,接地允许读和写。每一个设备都有自己的设备地址,AT24C02 也不例外,但是 AT24C02 的设备地址是包括不可编程部分和可编程部分,可编程部分是根据上图的硬件引脚 A0、A1 和 A2 所决定。设备地址最后一位用于设置数据的传输方向,即读操作/写操作,0 是写操作,1 是读操作,具体格式如下图 所示:

AT24C02设备地址格式图

二、AT24C02写时序

  AT24C02 支持 字节写模式页写模式字节写模式 就是一个地址一个数据写入。页写模式 就是连续写入数据。只需要写一个地址,连续写入数据时地址会自增,但存在页的限制,超过一页时,超出数据覆盖原先写入数据。

2.1、字节写模式

AT24C02字节写模式时序图

  主机在 I2C 总线发送第 1 个字节的数据为 AT24C02的设备地址 0xA0,用于寻找总线上找到 AT24C02,在获得 AT24C02 的应答信号之后,继续发送第 2 个字节数据,该字节数据是 AT24C02 的内存地址,再等到 AT24C02 的应答信号,主机继续发送第 3 字节数据,这里的数据即是写入在第 2 字节内存地址的数据。主机完成写操作后,可以发出停止信号,终止数据传输。

#define AT24C02_ADDRESS   0xA0

/**
 * @brief AT24C02写一个字节函数
 * 
 * @param address 待写入数据的内存地址
 * @param data 待写入的数据
 */
void AT24C02_WriteOneByte(uint8_t address, uint8_t data)
{
    I2C_Start();                                                                // 1、产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS);                                           // 2、发送写操作地址
    I2C_WaitAck();                                                              // 3、等待应答信号
    I2C_SendOneByte(address);                                                   // 4、发送内存地址
    I2C_WaitAck();                                                              // 5、等待应答信号
    I2C_SendOneByte(data);                                                      // 6、发送数据
    I2C_WaitAck();                                                              // 7、等待应答信号
    I2C_Stop();                                                                 // 8、产生停止信号

    Delay_ms(10);                                                               // 9、等待AT24C02写入完成
}

2.2、页写模式

AT24C02页写模式时序图

  在单字节写时序时,每次写入数据时都需要先写入设备的内存地址才能实现,在页写时序中,只需要告诉 AT24C02 第一个内存地址 1,后面数据会按照顺序写入到内存地址 2,内存地址 3等,大大节省了通信时间,提高了时效性。因为 AT24C02 每次只能 8bit 数据,所以它的页大小也就是 1 字节。页写时序的操作方式跟上面的单字节写时序差不多。

/**
 * @brief AT24C02在一页中写入多个字节
 * 
 * @param address 待写入数据的内存地址
 * @param pBuffer 待写入的数据
 * @param length 待写入数据的个数
 * 
 * @note AT24C02的页写模式存在页的限制,超过一页(8字节)时,超出数据覆盖原先写入数据
 */
void AT24C02_WriteOnePage(uint8_t address, uint8_t *pBuffer, uint8_t length)
{
    I2C_Start();                                                                // 1、产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS);                                           // 2、发送写操作地址
    I2C_WaitAck();                                                              // 3、等待应答信号
    I2C_SendOneByte(address);                                                   // 4、发送内存地址
    I2C_WaitAck();                                                              // 5、等待应答信号
    for (uint8_t i = 0; i < length; i++)
    {   
        I2C_SendOneByte(pBuffer[i]);                                            // 6、发送数据
        I2C_WaitAck();                                                          // 7、等待应答信号
    }
    I2C_Stop();                                                                 // 8、产生停止信号

    Delay_ms(10);                                                               // 9、等待AT24C02写入完成
}

三、AT24C02读时序

  AT24C02 支持 当前地址读模式随机地址读模式顺序读模式。AT24C02 的读操作会自动翻页。当前地址读模式 是基于上一次读/写操作的最后位置继续读出数据。随机地址读模式 是指定地址读出数据。顺序读模式 是连续读出数据。

3.1、当前地址读模式

  内部地址计数器保存着上次访问时最后一个地址加 1 的值。只要芯片有电,该地址就一直保存。当读到最后页的最后字节,地址会回转到 0;当写到某页尾的最后一个字节,地址会回转到该页的首字节。接收器件地址(读/写选择位为 "1")、EEPROM 应答 ACK 后,当前地址的数据就随时钟送出。主器件无需应答 "0",但需发送停止条件。

AT24C02当前地址读模式

3.2、随机地址读模式

AT24C02随机地址读模式

  AT24C02 读取数据的过程是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送 AT24C02 设备地址 0xA0,获取从机应答信号后,接着发送需要读取的内存地址;在读时序中,起始信号产生后,主机发送 AT24C02 设备地址 0xA1,获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,当主机发出的是非应答信号并以停止信号发出为结束,从机就会结束传输。

/**
 * @brief AT24C02读一个字节函数
 * 
 * @param address 待读取数据的内存地址
 * @return uint8_t 读取的数据
 */
uint8_t AT24C02_ReadOneByte(uint8_t address)
{
    uint8_t receive = 0;

    I2C_Start();                                                                // 1、产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS);                                           // 2、发送写操作地址
    I2C_WaitAck();                                                              // 3、等待应答信号
    I2C_SendOneByte(address);                                                   // 4、发送内存地址
    I2C_WaitAck();                                                              // 5、等待应答信号
    I2C_Start();                                                                // 6、再次产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS | 1);                                       // 7、发送读操作地址
    I2C_WaitAck();                                                              // 8、等待应答信号
    receive = I2C_ReadOneByte(1);                                               // 9、读取数据,并发送非应答信号
    I2C_Stop();                                                                 // 10、产生停止信号

    return receive;                                                             // 11、返回读取的数据
}

3.3、顺序读模式

  顺序读可以通过 “当前地址读” 或 “随机读” 启动。主器件接收到一个数据后,应答 ACK。只要 EEPROM 接收到 ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器地址末尾,地址自动回转到 0,仍可继续顺序读取数据。主器件发送非应答信号,即可结束顺序读操。

AT24C02顺序读模式

/**
 * @brief AT24C02顺序读取多个字节数据函数
 * 
 * @param address 起始地址
 * @param pBuffer 保存读取的数据的缓冲区
 * @param length 读取字节的个数
 */
void AT24C02_ReadBytes(uint8_t address, uint8_t *pBuffer, uint16_t length)
{
    I2C_Start();                                                                // 1、产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS);                                           // 2、发送写操作地址
    I2C_WaitAck();                                                              // 3、等待应答信号
    I2C_SendOneByte(address);                                                   // 4、发送内存地址
    I2C_WaitAck();                                                              // 5、等待应答信号
    I2C_Start();                                                                // 6、再次产生起始信号
    I2C_SendOneByte(AT24C02_ADDRESS | 1);                                       // 7、发送读操作地址
    I2C_WaitAck();                                                              // 8、等待应答信号
    for (uint8_t i = 0; i < length; i++)
    {
        if (i < length - 1)
        {
            pBuffer[i] = I2C_ReadOneByte(0);                                    // 9、读取数据,并发送应答信号
        }
        else
        {
            pBuffer[i] = I2C_ReadOneByte(1);                                    // 10、读取最后一个数据,并发送非应答信号
        }
    }
  
    I2C_Stop();                                                                 // 11、产生停止信号
}

四、源码实现

4.1、原理图

AT24C02原理图

AT24C02接线图

  从原理图上,我们可以看出 AT24C02 的 A0、A1 和 A2 引脚都接地,因此 AT24C02 的设备地址为 1010000

4.2、程序源码

  I2C1 初始化函数内容如下:

I2C_HandleTypeDef g_i2c1_handle;

/**
 * @brief I2C1初始化
 * 
 * @param speed SCL的时钟频率,此值要低于400000
 */
void I2C1_Init(uint32_t speed)
{
    g_i2c1_handle.Instance = I2C1;                                                  // 使用的I2C
    g_i2c1_handle.Init.ClockSpeed = speed;                                      // SCL时钟频率
    g_i2c1_handle.Init.DutyCycle = I2C_DUTYCYCLE_2;                             // 时钟占空比
    g_i2c1_handle.Init.OwnAddress1 = 0;                                         // STM32自身设备地址1
    g_i2c1_handle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;                // 地址模式
    g_i2c1_handle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;               // 双地址模式
    g_i2c1_handle.Init.OwnAddress2 = 0;                                         // STM32自身设备地址2
    g_i2c1_handle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;               // 通用广播地址
    g_i2c1_handle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;                   // 禁止时钟延长模式
   
    HAL_I2C_Init(&g_i2c1_handle);
}

  I2C 底层初始化函数内容如下:

/**
 * @brief I2C底层初始化函数
 * 
 * @param hi2c I2C句柄
 */
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    if(hi2c->Instance == I2C1)
    {
	__HAL_RCC_I2C1_CLK_ENABLE();                                            // 使能I2C1时钟
        __HAL_RCC_GPIOB_CLK_ENABLE();                                           // 使能I2C1对应的GPIO时钟

        GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;                          // SCL引脚和SDA引脚
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;                                 // 复用开漏输出
        GPIO_InitStruct.Pull = GPIO_PULLUP;                                     // 使用上拉电阻
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;                           // 输出速度
        GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;                              // 复用功能
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}

  AT24C02 一次写入多个字节函数内容如下:

/**
 * @brief AT24C02在一页中写入多个字节
 * 
 * @param hi2c I2C句柄
 * @param address 待写入数据的内存地址
 * @param pData 待写入的数据
 * @param length 待写入数据的个数
 */
void AT24C02_WriteData(I2C_HandleTypeDef *hi2c, uint16_t address, uint8_t *pData, uint16_t length)
{
    HAL_I2C_Mem_Write(hi2c, AT24C02_ADDRESS, address, I2C_MEMADD_SIZE_8BIT, pData, length, 1000);
    HAL_Delay(5);
}

  AT24C02 一次读取多个字节函数内容如下:

/**
 * @brief AT24C02顺序读取多个字节数据函数
 * 
 * @param hi2c I2C句柄
 * @param address 起始地址
 * @param pData 保存读取的数据的缓冲区
 * @param length 读取字节的个数
 */
void AT24C02_ReadData(I2C_HandleTypeDef *hi2c, uint16_t address, uint8_t *pData, uint16_t length)
{
    HAL_I2C_Mem_Read(hi2c, AT24C02_ADDRESS, address, I2C_MEMADD_SIZE_8BIT, pData, length, 1000);
}

  有关时钟配置函数请在 STM32 的时钟系统 篇章查看。

  有关 USART1 的配置请在 串口通信 篇章查看。

  main() 函数内容如下:

int main(void)
{
    uint8_t data[10] = {0};

    HAL_Init();

    System_Clock_Init(8, 336, 2, 7);
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);                         // 设置中断优先级分组

    USART1_Init(115200);
    I2C1_Init(100000);

    AT24C02_WriteData(&g_i2c1_handle, 8, (uint8_t *)"123456789", 10);
    AT24C02_ReadData(&g_i2c1_handle, 8, data, 10);
    printf("%s\r\n", data);

    while (1)
    {

    }
  
    return 0;
}

标签:字节,应答,param,地址,AT24C02,I2C,EEPROM,14
From: https://www.cnblogs.com/kurome/p/18076305

相关文章

  • 【LeetCode 1466】重新规划路线
    题目描述原题链接:LeetCode.1466重新规划路线解题思路路线网形成一棵树,说明每个节点都参与两条路线,或作为起点或作为终点;要想所有城市都可以通往城市0,必须要把所有逆向的路线都变更一次方向,逆向路线总数量即为答案;朴素BFS版本从城市0出发,遍历每个从已通城市出发的......
  • 2014-2023年各地级市空气质量指数AQI指数日度数据
    2014-2023年各地级市空气质量指数AQI指数日度数据1、时间:2014-2023.3.82、来源:https://www.qweather.com/air/beiliu-101300903.htm3、指标:统计日期、地区编码ID、地区代码、地区名称、AQI指数、空气质量级别、首要污染物4、样本量:100W+5、范围:300+地级市6、指标解释:空......
  • L2-014 列车调度
    法一:(23分)数组。有一个测试点会超时,每次第二层遍历是O(n)。#include<bits/stdc++.h>usingnamespacestd;inta[100010];intmain(){ intn,t,count=0; cin>>n; for(inti=0;i<n;i++){ cin>>t; boolflag=false; for(inti=0;i<count;i++){ if(a[i]>t......
  • 14. 最长公共前缀c
    char*longestCommonPrefix(char**strs,intstrsSize){intindex=1,min=INT_MAX;if(strsSize==1)returnstrs[0];while(index<strsSize){inti=0;while(strs[index-1][i]!=0&&strs[index][i]!=0&&strs[index-1][......
  • 2024-03-14 leetcode写题记录
    目录2024-03-14leetcode写题记录829.连续整数求和题目链接题意解法2024-03-14leetcode写题记录829.连续整数求和题目链接829.连续整数求和题意给定一个正整数\(n\),返回连续正整数满足所有数字之和为\(n\)的组数。示例1:输入:n=5输出:2解释:5=2+3,共有两......
  • GEE C14 Aggregating Images for Time Series 聚合时间序列图像
    一、CHIRPS数据CHIRPS: theClimateHazardsGroupInfraRedPrecipitationwithStation,全称“气候危害群红外线降水与站点数据”,该数据可利用时间能够追溯到1981年,目前仍然在更新当中,主要用于研究人员分析特定空间在特定时间段内降雨量的变化趋势,从而广泛应用于干旱监测。CH......
  • 【2024-03-14】考虑外包
    20:00习惯性的悲观想法会使更多不顺利的事降临到我们头.上。而且这种想法会使我们很容易陷入抑郁状态,使我们不能发挥出原有的能力。悲观的预言常常是自我实现的。                                   ......
  • 信息学奥赛一本通:1146:判断字符串是否为回文
    【题目描述】输入一个字符串,输出该字符串是否回文。回文是指顺读和倒读都一样的字符串。【输入】输入为一行字符串(字符串中没有空白字符,字符串长度不超过100)。【输出】如果字符串是回文,输出yes;否则,输出no。【输入样例】abcdedcba【输出样例】yes【参考程序......
  • 2024.3.14
    按值查找按位置查找链表释放链表逆置......
  • 3月14-第五讲复习回顾和第六讲TCP协议
    结合gpt和其他方面的资料,对于昨天的网络层和链路层做出补充:1.数据传输时两层基本都经过,网络层规划路由表(路由器跳转路径),装配ip地址(用来规划线路),封装和传输数据包在节点与目标设备之间,把链路层的数据帧转换为数据包。链路层则是节点之间的,物理层面,使用Mac这种物理地址定位。2.注......