微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 第050例:I2C-EEPROM

第050例:I2C-EEPROM

时间:10-02 整理:3721RD 点击:
【HAL库每天一例】系列例程从今天开始持续更新。
我们将坚持每天至少发布一个基于YS-F1Pro开发板的HAL库例程,
该系列例程将带领大家从零开始使用HAL库,后面会持续添加模块应用例程。
同样的,我们还程序发布基于HAL库的指导文档和视频教程,欢迎持续关注,并提出改进意见。
例程下载:
资料包括程序、相关说明资料以及软件使用截图
链接:http://pan.baidu.com/s/1i574oPv
密码:r3s3

(硬石YS-F1Pro开发板HAL库例程持续更新\1. 软件设计之基本裸机例程(HAL库版本)\YSF1_HAL-050. I2C-EEPROM)
/**
  ******************************************************************************
  *                           硬石YS-F1Pro开发板例程功能说明
  *
  *  例程名称: 1. EEPROM(AT24C02)-硬件I2C
  *   
  ******************************************************************************
  * 说明:
  * 本例程配套硬石stm32开发板YS-F1Pro使用。
  *
  * 淘宝:
  * 论坛:http://www.ing10bbs.com
  * 版权归硬石嵌入式开发团队所有,请勿商用。
  ******************************************************************************
  */
【1】例程简介
  I2C总线是飞利浦公司开发的两线式串行总线。用于连接微控制器和外围设备。它是同步通信的一
种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。通过串行数据
(SDA)线和串行时钟 (SCL)线在连接到总线的器件间传递信息。每个器件都有一个唯一的地址识
别,而且都可以作为一个发送器或接收器。
  EEPROM(Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储
器--一种掉电后数据不丢失的存储芯片。EEPROM可以擦除已有信息,重新编程。一般用在即插即用。
YS-F1Pro开发板上集成一个型号为AT24C02的EEPROM,本例程实现对它的读写操作。
  
【2】跳线帽情况
******* 为保证例程正常运行,必须插入以下跳线帽 **********
丝印编号     IO端口      目标功能引脚        出厂默认设置
  JP1        PA10        TXD(CH340G)          已接入
  JP2        PA9         RXD(CH340G)          已接入
  
【3】操作及现象
使用开发板配套的MINI USB线连接到开发板标示“调试串口”字样的MIMI USB接口(需要安装驱动),
在电脑端打开串口调试助手工具,设置参数为115200 8-N-1。下载完程序之后,在串口调试助手窗口
可接收到信息。
/******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/




bsp_EEPROM.h文件内容

  1. #ifndef __I2C_EEPROM_H__
  2. #define        __I2C_EEPROM_H__
  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include "stm32f1xx_hal.h"

  5. /* 类型定义 ------------------------------------------------------------------*/
  6. /* 宏定义 --------------------------------------------------------------------*/
  7. #define I2C_OWN_ADDRESS                            0x0A              // stm32本机I2C地址
  8. #define I2C_SPEEDCLOCK                             400000            // I2C通信速率(最大为400K)
  9. #define I2C_DUTYCYCLE                              I2C_DUTYCYCLE_2   // I2C占空比模式:1/2

  10. #define EEPROM_I2Cx                                I2C1
  11. #define EEPROM_I2C_RCC_CLK_ENABLE()                __HAL_RCC_I2C1_CLK_ENABLE()
  12. #define EEPROM_I2C_RCC_CLK_DISABLE()               __HAL_RCC_I2C1_CLK_DISABLE()

  13. #define EEPROM_I2C_GPIO_CLK_ENABLE()               __HAL_RCC_GPIOB_CLK_ENABLE()
  14. #define EEPROM_I2C_GPIO_CLK_DISABLE()              __HAL_RCC_GPIOB_CLK_DISABLE()   
  15. #define EEPROM_I2C_GPIO_PORT                       GPIOB   
  16. #define EEPROM_I2C_SCL_PIN                         GPIO_PIN_6
  17. #define EEPROM_I2C_SDA_PIN                         GPIO_PIN_7

  18. /*
  19. * EEPROM 2kb = 2048bit = 2048/8 B = 256 B
  20. * 32 pages of 8 bytes each
  21. *
  22. * Device Address
  23. * 1 0 1 0 A2 A1 A0 R/W
  24. * 1 0 1 0 0  0  0  0 = 0XA0
  25. * 1 0 1 0 0  0  0  1 = 0XA1
  26. */
  27. /* EEPROM Addresses defines */
  28. #define EEPROM_I2C_ADDRESS                         0xA0

  29. /* 扩展变量 ------------------------------------------------------------------*/
  30. extern I2C_HandleTypeDef hi2c_eeprom;

  31. /* 函数声明 ------------------------------------------------------------------*/
  32. void               MX_I2C_EEPROM_Init(void);
  33. void               I2C_EEPROM_WriteData(uint16_t Addr, uint8_t Reg, uint8_t Value);
  34. HAL_StatusTypeDef  I2C_EEPROM_WriteBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length);
  35. uint8_t            I2C_EEPROM_ReadData(uint16_t Addr, uint8_t Reg);
  36. HAL_StatusTypeDef  I2C_EEPROM_ReadBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length);
  37. HAL_StatusTypeDef  I2C_EEPROM_IsDeviceReady(uint16_t DevAddress, uint32_t Trials);

  38. #endif /* __I2C_EEPROM_H__ */

  39. /******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/

复制代码


bsp_EEPROM.c文件内容

  1. /**
  2.   ******************************************************************************
  3.   * 文件名程: bsp_EEPROM.c
  4.   * 作    者: 硬石嵌入式开发团队
  5.   * 版    本: V1.0
  6.   * 编写日期: 2015-10-04
  7.   * 功    能: 板载EEPROM(AT24C02)底层驱动程序
  8.   ******************************************************************************
  9.   * 说明:
  10.   * 本例程配套硬石stm32开发板YS-F1Pro使用。
  11.   *
  12.   * 淘宝:
  13.   * 论坛:http://www.ing10bbs.com
  14.   * 版权归硬石嵌入式开发团队所有,请勿商用。
  15.   ******************************************************************************
  16.   */
  17. /* 包含头文件 ----------------------------------------------------------------*/
  18. #include "i2c/bsp_EEPROM.h"

  19. /* 私有类型定义 --------------------------------------------------------------*/
  20. /* 私有宏定义 ----------------------------------------------------------------*/
  21. #define EVAL_I2Cx_TIMEOUT_MAX                   3000

  22. /* 私有变量 ------------------------------------------------------------------*/
  23. I2C_HandleTypeDef hi2c_eeprom;
  24. uint32_t I2cxTimeout = EVAL_I2Cx_TIMEOUT_MAX;

  25. /* 扩展变量 ------------------------------------------------------------------*/
  26. /* 私有函数原形 --------------------------------------------------------------*/
  27. /* 函数体 --------------------------------------------------------------------*/
  28. /**
  29.   * 函数功能: I2C外设初始化
  30.   * 输入参数: 无
  31.   * 返 回 值: 无
  32.   * 说    明:无
  33.   */
  34. void MX_I2C_EEPROM_Init(void)
  35. {
  36.   hi2c_eeprom.Instance             = EEPROM_I2Cx;
  37.   hi2c_eeprom.Init.ClockSpeed      = I2C_SPEEDCLOCK;
  38.   hi2c_eeprom.Init.DutyCycle       = I2C_DUTYCYCLE;
  39.   hi2c_eeprom.Init.OwnAddress1     = 0;
  40.   hi2c_eeprom.Init.AddressingMode  = I2C_ADDRESSINGMODE_7BIT;
  41.   hi2c_eeprom.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  42.   hi2c_eeprom.Init.OwnAddress2     = 0;
  43.   hi2c_eeprom.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  44.   hi2c_eeprom.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;
  45.   HAL_I2C_Init(&hi2c_eeprom);
  46. }

  47. /**
  48.   * 函数功能: I2C外设硬件初始化配置
  49.   * 输入参数: hi2c:I2C句柄类型指针
  50.   * 返 回 值: 无
  51.   * 说    明: 该函数被HAL库内部调用
  52.   */
  53. void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
  54. {
  55.   GPIO_InitTypeDef GPIO_InitStruct;
  56.   if(hi2c->Instance==EEPROM_I2Cx)
  57.   {  
  58.     /* 使能外设时钟 */
  59.     EEPROM_I2C_RCC_CLK_ENABLE();        
  60.     EEPROM_I2C_GPIO_CLK_ENABLE();
  61.    
  62.     /**I2C1 GPIO Configuration   
  63.     PB6     ------> I2C1_SCL
  64.     PB7     ------> I2C1_SDA
  65.     */
  66.     GPIO_InitStruct.Pin = EEPROM_I2C_SCL_PIN|EEPROM_I2C_SDA_PIN;
  67.     GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  68.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  69.     HAL_GPIO_Init(EEPROM_I2C_GPIO_PORT, &GPIO_InitStruct);
  70.   }
  71. }

  72. /**
  73.   * 函数功能: I2C外设硬件反初始化配置
  74.   * 输入参数: hi2c:I2C句柄类型指针
  75.   * 返 回 值: 无
  76.   * 说    明: 该函数被HAL库内部调用
  77.   */
  78. void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
  79. {
  80.   if(hi2c->Instance==EEPROM_I2Cx)
  81.   {
  82.     /* 禁用外设时钟 */
  83.     EEPROM_I2C_GPIO_CLK_DISABLE();
  84.   
  85.     /**I2C1 GPIO Configuration   
  86.     PB6     ------> I2C1_SCL
  87.     PB7     ------> I2C1_SDA
  88.     */
  89.     HAL_GPIO_DeInit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN|EEPROM_I2C_SDA_PIN);
  90.   }
  91. }


  92. /**
  93.   * 函数功能: I2C通信错误处理函数
  94.   * 输入参数: 无
  95.   * 返 回 值: 无
  96.   * 说    明: 一般在I2C通信超时时调用该函数
  97.   */
  98. static void I2C_EEPROM_Error (void)
  99. {
  100.   /* 反初始化I2C通信总线 */
  101.   HAL_I2C_DeInit(&hi2c_eeprom);
  102.   
  103.   /* 重新初始化I2C通信总线*/
  104.   MX_I2C_EEPROM_Init();
  105.   printf("EEPROM I2C通信超时! 重新启动I2C...\n");
  106. }

  107. /**
  108.   * 函数功能: 通过I2C写入一个值到指定寄存器内
  109.   * 输入参数: Addr:I2C设备地址
  110.   *           Reg:目标寄存器
  111.   *           Value:值
  112.   * 返 回 值: 无
  113.   * 说    明: 无
  114.   */
  115. void I2C_EEPROM_WriteData(uint16_t Addr, uint8_t Reg, uint8_t Value)
  116. {
  117.   HAL_StatusTypeDef status = HAL_OK;
  118.   
  119.   status = HAL_I2C_Mem_Write(&hi2c_eeprom, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1, I2cxTimeout);
  120.   
  121.   /* 检测I2C通信状态 */
  122.   if(status != HAL_OK)
  123.   {
  124.     /* 调用I2C通信错误处理函数 */
  125.     I2C_EEPROM_Error();
  126.   }
  127. }

  128. /**
  129.   * 函数功能: 通过I2C写入一段数据到指定寄存器内
  130.   * 输入参数: Addr:I2C设备地址
  131.   *           Reg:目标寄存器
  132.   *           RegSize:寄存器尺寸(8位或者16位)
  133.   *           pBuffer:缓冲区指针
  134.   *           Length:缓冲区长度
  135.   * 返 回 值: HAL_StatusTypeDef:操作结果
  136.   * 说    明: 在循环调用是需加一定延时时间
  137.   */
  138. HAL_StatusTypeDef I2C_EEPROM_WriteBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length)
  139. {
  140.   HAL_StatusTypeDef status = HAL_OK;
  141.   
  142.   status = HAL_I2C_Mem_Write(&hi2c_eeprom, Addr, (uint16_t)Reg, RegSize, pBuffer, Length, I2cxTimeout);

  143.   /* 检测I2C通信状态 */
  144.   if(status != HAL_OK)
  145.   {
  146.     /* 调用I2C通信错误处理函数 */
  147.     I2C_EEPROM_Error();
  148.   }        
  149.   return status;
  150. }


  151. /**
  152.   * 函数功能: 通过I2C读取一个指定寄存器内容
  153.   * 输入参数: Addr:I2C设备地址
  154.   *           Reg:目标寄存器
  155.   * 返 回 值: uint8_t:寄存器内容
  156.   * 说    明: 无
  157.   */
  158. uint8_t I2C_EEPROM_ReadData(uint16_t Addr, uint8_t Reg)
  159. {
  160.   HAL_StatusTypeDef status = HAL_OK;
  161.   uint8_t value = 0;
  162.   
  163.   status = HAL_I2C_Mem_Read(&hi2c_eeprom, Addr, Reg, I2C_MEMADD_SIZE_8BIT, &value, 1, I2cxTimeout);

  164.   /* 检测I2C通信状态 */
  165.   if(status != HAL_OK)
  166.   {
  167.     /* 调用I2C通信错误处理函数 */
  168.     I2C_EEPROM_Error();
  169.   
  170.   }
  171.   return value;
  172. }

  173. /**
  174.   * 函数功能: 通过I2C读取一段寄存器内容存放到指定的缓冲区内
  175.   * 输入参数: Addr:I2C设备地址
  176.   *           Reg:目标寄存器
  177.   *           RegSize:寄存器尺寸(8位或者16位)
  178.   *           pBuffer:缓冲区指针
  179.   *           Length:缓冲区长度
  180.   * 返 回 值: HAL_StatusTypeDef:操作结果
  181.   * 说    明: 无
  182.   */
  183. HAL_StatusTypeDef I2C_EEPROM_ReadBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length)
  184. {
  185.   HAL_StatusTypeDef status = HAL_OK;

  186.   status = HAL_I2C_Mem_Read(&hi2c_eeprom, Addr, (uint16_t)Reg, RegSize, pBuffer, Length, I2cxTimeout);
  187.   
  188.   /* 检测I2C通信状态 */
  189.   if(status != HAL_OK)
  190.   {
  191.     /* 调用I2C通信错误处理函数 */
  192.     I2C_EEPROM_Error();
  193.   }        
  194.   return status;
  195. }

  196. /**
  197.   * 函数功能: 检测I2C设备是否处于准备好可以通信状态
  198.   * 输入参数: DevAddress:I2C设备地址
  199.   *           Trials:尝试测试次数
  200.   * 返 回 值: HAL_StatusTypeDef:操作结果
  201.   * 说    明: 无
  202.   */
  203. HAL_StatusTypeDef I2C_EEPROM_IsDeviceReady(uint16_t DevAddress, uint32_t Trials)
  204. {
  205.   return (HAL_I2C_IsDeviceReady(&hi2c_eeprom, DevAddress, Trials, I2cxTimeout));
  206. }

  207. /******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/

复制代码





2. EEPROM(AT24C02)-软件模拟I2C(建议使用)
bsp_EEPROM.h文件内容

  1. #ifndef __I2C_EEPROM_H__
  2. #define        __I2C_EEPROM_H__
  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include "stm32f1xx_hal.h"

  5. /* 类型定义 ------------------------------------------------------------------*/
  6. /* 宏定义 --------------------------------------------------------------------*/
  7. #define I2C_OWN_ADDRESS                            0x0A

  8. #define I2C_WR                0                /* 写控制bit */
  9. #define I2C_RD                1                /* 读控制bit */

  10. #define I2C_GPIO_CLK_ENABLE()               __HAL_RCC_GPIOB_CLK_ENABLE()
  11. #define I2C_GPIO_PORT                       GPIOB   
  12. #define I2C_SCL_PIN                         GPIO_PIN_6
  13. #define I2C_SDA_PIN                         GPIO_PIN_7

  14. #define I2C_SCL_HIGH()                      HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_SET)    // 输出高电平
  15. #define I2C_SCL_LOW()                       HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_RESET)  // 输出低电平
  16. #define I2C_SDA_HIGH()                      HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_SET)    // 输出高电平
  17. #define I2C_SDA_LOW()                       HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_RESET)  // 输出低电平
  18. #define I2C_SDA_READ()                      HAL_GPIO_ReadPin(I2C_GPIO_PORT,I2C_SDA_PIN)

  19. /*
  20. * AT24C02 2kb = 2048bit = 2048/8 B = 256 B
  21. * 32 pages of 8 bytes each
  22. *
  23. * Device Address
  24. * 1 0 1 0 A2 A1 A0 R/W
  25. * 1 0 1 0 0  0  0  0 = 0XA0
  26. * 1 0 1 0 0  0  0  1 = 0XA1
  27. */

  28. /* AT24C01/02每页有8个字节
  29. * AT24C04/08A/16A每页有16个字节
  30. */
  31. #define EEPROM_DEV_ADDR                           0xA0                    /* 24xx02的设备地址 */
  32. #define EEPROM_PAGE_SIZE                      8                          /* 24xx02的页面大小 */
  33. #define EEPROM_SIZE                                      256                          /* 24xx02总容量 */

  34. /* 扩展变量 ------------------------------------------------------------------*/
  35. /* 函数声明 ------------------------------------------------------------------*/
  36. void    I2C_Start(void);
  37. void    I2C_Stop(void);
  38. void    I2C_SendByte(uint8_t _ucByte);
  39. uint8_t I2C_ReadByte(void);
  40. uint8_t I2C_WaitAck(void);
  41. void    I2C_Ack(void);
  42. void    I2C_NAck(void);
  43. uint8_t I2C_CheckDevice(uint8_t _Address);

  44. uint8_t EEPROM_CheckOk(void);
  45. uint8_t EEPROM_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);
  46. uint8_t EEPROM_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);



  47. #endif /* __I2C_EEPROM_H__ */

  48. /******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/

复制代码


bsp_EEPROM.c文件内容

  1. /**
  2.   ******************************************************************************
  3.   * 文件名程: bsp_eeprom.c
  4.   * 作    者: 硬石嵌入式开发团队
  5.   * 版    本: V1.0
  6.   * 编写日期: 2015-10-04
  7.   * 功    能: 板载EEPROM(AT24C02)底层驱动程序
  8.   ******************************************************************************
  9.   * 说明:
  10.   * 本例程配套硬石stm32开发板YS-F1Pro使用。
  11.   *
  12.   * 淘宝:
  13.   * 论坛:http://www.ing10bbs.com
  14.   * 版权归硬石嵌入式开发团队所有,请勿商用。
  15.   ******************************************************************************
  16.   */
  17. /* 包含头文件 ----------------------------------------------------------------*/
  18. #include "i2c/bsp_EEPROM.h"

  19. /* 私有类型定义 --------------------------------------------------------------*/
  20. /* 私有宏定义 ----------------------------------------------------------------*/
  21. /* 私有变量 ------------------------------------------------------------------*/
  22. /* 扩展变量 ------------------------------------------------------------------*/
  23. /* 私有函数原形 --------------------------------------------------------------*/
  24. /* 函数体 --------------------------------------------------------------------*/
  25. /**
  26.   * 函数功能: I2C总线位延迟,最快400KHz
  27.   * 输入参数: 无
  28.   * 返 回 值: 无
  29.   * 说    明:无
  30.   */
  31. static void I2C_Delay(void)
  32. {
  33.         uint8_t i;

  34.         /* 
  35.                  下面的时间是通过逻辑分析仪测试得到的。
  36.                 CPU主频72MHz时,在内部Flash运行, MDK工程不优化
  37.                 循环次数为10时,SCL频率 = 205KHz
  38.                 循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
  39.                  循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
  40.         
  41.     IAR工程编译效率高,不能设置为7
  42.         */
  43.         for (i = 0; i < 10; i++);
  44. }

  45. /**
  46.   * 函数功能: CPU发起I2C总线启动信号
  47.   * 输入参数: 无
  48.   * 返 回 值: 无
  49.   * 说    明:无
  50.   */
  51. void I2C_Start(void)
  52. {
  53.         /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
  54.         I2C_SDA_HIGH();
  55.         I2C_SCL_HIGH();
  56.         I2C_Delay();
  57.         I2C_SDA_LOW();
  58.         I2C_Delay();
  59.         I2C_SCL_LOW();
  60.         I2C_Delay();
  61. }

  62. /**
  63.   * 函数功能: CPU发起I2C总线停止信号
  64.   * 输入参数: 无
  65.   * 返 回 值: 无
  66.   * 说    明:无
  67.   */
  68. void I2C_Stop(void)
  69. {
  70.         /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
  71.         I2C_SDA_LOW();
  72.         I2C_SCL_HIGH();
  73.         I2C_Delay();
  74.         I2C_SDA_HIGH();
  75. }

  76. /**
  77.   * 函数功能: CPU向I2C总线设备发送8bit数据
  78.   * 输入参数: Byte : 等待发送的字节
  79.   * 返 回 值: 无
  80.   * 说    明:无
  81.   */
  82. void I2C_SendByte(uint8_t Byte)
  83. {
  84.         uint8_t i;

  85.         /* 先发送字节的高位bit7 */
  86.         for (i = 0; i < 8; i++)
  87.         {               
  88.                 if (Byte & 0x80)
  89.                 {
  90.                         I2C_SDA_HIGH();
  91.                 }
  92.                 else
  93.                 {
  94.                         I2C_SDA_LOW();
  95.                 }
  96.                 I2C_Delay();
  97.                 I2C_SCL_HIGH();
  98.                 I2C_Delay();        
  99.                 I2C_SCL_LOW();
  100.                 if (i == 7)
  101.                 {
  102.                         I2C_SDA_HIGH(); // 释放总线
  103.                 }
  104.                 Byte <<= 1;        /* 左移一个bit */
  105.                 I2C_Delay();
  106.         }
  107. }


  108. /**
  109.   * 函数功能: CPU从I2C总线设备读取8bit数据
  110.   * 输入参数: 无
  111.   * 返 回 值: 读到的数据
  112.   * 说    明:无
  113.   */
  114. uint8_t I2C_ReadByte(void)
  115. {
  116.         uint8_t i;
  117.         uint8_t value;

  118.         /* 读到第1个bit为数据的bit7 */
  119.         value = 0;
  120.         for (i = 0; i < 8; i++)
  121.         {
  122.                 value <<= 1;
  123.                 I2C_SCL_HIGH();
  124.                 I2C_Delay();
  125.                 if (I2C_SDA_READ())
  126.                 {
  127.                         value++;
  128.                 }
  129.                 I2C_SCL_LOW();
  130.                 I2C_Delay();
  131.         }
  132.         return value;
  133. }

  134. /**
  135.   * 函数功能: CPU产生一个时钟,并读取器件的ACK应答信号
  136.   * 输入参数: 无
  137.   * 返 回 值: 返回0表示正确应答,1表示无器件响应
  138.   * 说    明:无
  139.   */
  140. uint8_t I2C_WaitAck(void)
  141. {
  142.         uint8_t re;

  143.         I2C_SDA_HIGH();        /* CPU释放SDA总线 */
  144.         I2C_Delay();
  145.         I2C_SCL_HIGH();        /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
  146.         I2C_Delay();
  147.         if (I2C_SDA_READ())        /* CPU读取SDA口线状态 */
  148.         {
  149.                 re = 1;
  150.         }
  151.         else
  152.         {
  153.                 re = 0;
  154.         }
  155.         I2C_SCL_LOW();
  156.         I2C_Delay();
  157.         return re;
  158. }

  159. /**
  160.   * 函数功能: CPU产生一个ACK信号
  161.   * 输入参数: 无
  162.   * 返 回 值: 无
  163.   * 说    明:无
  164.   */
  165. void I2C_Ack(void)
  166. {
  167.         I2C_SDA_LOW();        /* CPU驱动SDA = 0 */
  168.         I2C_Delay();
  169.         I2C_SCL_HIGH();        /* CPU产生1个时钟 */
  170.         I2C_Delay();
  171.         I2C_SCL_LOW();
  172.         I2C_Delay();
  173.         I2C_SDA_HIGH();        /* CPU释放SDA总线 */
  174. }

  175. /**
  176.   * 函数功能: CPU产生1个NACK信号
  177.   * 输入参数: 无
  178.   * 返 回 值: 无
  179.   * 说    明:无
  180.   */
  181. void I2C_NAck(void)
  182. {
  183.         I2C_SDA_HIGH();        /* CPU驱动SDA = 1 */
  184.         I2C_Delay();
  185.         I2C_SCL_HIGH();        /* CPU产生1个时钟 */
  186.         I2C_Delay();
  187.         I2C_SCL_LOW();
  188.         I2C_Delay();        
  189. }

  190. /**
  191.   * 函数功能: 配置I2C总线的GPIO,采用模拟IO的方式实现
  192.   * 输入参数: 无
  193.   * 返 回 值: 无
  194.   * 说    明:无
  195.   */
  196. static void I2C_InitGPIO(void)
  197. {
  198.   GPIO_InitTypeDef GPIO_InitStruct;
  199.   
  200.   /* 打开GPIO时钟 */
  201.         I2C_GPIO_CLK_ENABLE();

  202.   GPIO_InitStruct.Pin = I2C_SCL_PIN|I2C_SDA_PIN;
  203.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  204.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
  205.   HAL_GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStruct);

  206.   /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
  207.   I2C_Stop();
  208. }

  209. /**
  210.   * 函数功能: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
  211.   * 输入参数: _Address:设备的I2C总线地址
  212.   * 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
  213.   * 说    明:在访问I2C设备前,请先调用 I2C_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO
  214.   */
  215. uint8_t I2C_CheckDevice(uint8_t _Address)
  216. {
  217.         uint8_t ucAck;

  218.         I2C_InitGPIO();                /* 配置GPIO */        
  219.         I2C_Start();                /* 发送启动信号 */
  220.         /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
  221.         I2C_SendByte(_Address | I2C_WR);
  222.         ucAck = I2C_WaitAck();        /* 检测设备的ACK应答 */
  223.         I2C_Stop();                        /* 发送停止信号 */
  224.         return ucAck;
  225. }

  226. /**
  227.   * 函数功能: 判断串行EERPOM是否正常
  228.   * 输入参数: 无
  229.   * 返 回 值: 1 表示正常, 0 表示不正常
  230.   * 说    明:无
  231.   */
  232. uint8_t EEPROM_CheckOk(void)
  233. {
  234.         if(I2C_CheckDevice(EEPROM_DEV_ADDR) == 0)
  235.         {
  236.                 return 1;
  237.         }
  238.         else
  239.         {
  240.                 /* 失败后,切记发送I2C总线停止信号 */
  241.                 I2C_Stop();               
  242.                 return 0;
  243.         }
  244. }

  245. /**
  246.   * 函数功能: 从串行EEPROM指定地址处开始读取若干数据
  247.   * 输入参数: ReadBuf : 存放读到的数据的缓冲区指针
  248.   *           Address : 起始地址  
  249.   *           Size : 数据长度,单位为字节
  250.   * 返 回 值:  0 表示失败,1表示成功
  251.   * 说    明:无
  252.   */
  253. uint8_t EEPROM_ReadBytes(uint8_t *ReadBuf, uint16_t Address, uint16_t Size)
  254. {
  255.         uint16_t i;
  256.         
  257.         /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
  258.         
  259.         /* 第1步:发起I2C总线启动信号 */
  260.         I2C_Start();        
  261.   
  262.         /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  263.         I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);        /* 此处是写指令 */        
  264.   
  265.         /* 第3步:等待ACK */
  266.         if (I2C_WaitAck() != 0)
  267.         {
  268.                 goto cmd_fail;        /* EEPROM器件无应答 */
  269.         }
  270.   
  271.         /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
  272.         I2C_SendByte((uint8_t)Address);        
  273.   
  274.         /* 第5步:等待ACK */
  275.         if (I2C_WaitAck() != 0)
  276.         {
  277.                 goto cmd_fail;        /* EEPROM器件无应答 */
  278.         }        
  279.         /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
  280.         I2C_Start();        
  281.   
  282.         /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  283.         I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD);        /* 此处是读指令 */
  284.         
  285.         /* 第8步:发送ACK */
  286.         if (I2C_WaitAck() != 0)
  287.         {
  288.                 goto cmd_fail;        /* EEPROM器件无应答 */
  289.         }        
  290.         
  291.         /* 第9步:循环读取数据 */
  292.         for (i = 0; i < Size; i++)
  293.         {
  294.                 ReadBuf[i] = I2C_ReadByte();        /* 读1个字节 */
  295.                
  296.                 /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
  297.                 if (i != Size - 1)
  298.                 {
  299.                         I2C_Ack();        /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
  300.                 }
  301.                 else
  302.                 {
  303.                         I2C_NAck();        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  304.                 }
  305.         }
  306.         /* 发送I2C总线停止信号 */
  307.         I2C_Stop();
  308.         return 1;        /* 执行成功 */

  309. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  310.         /* 发送I2C总线停止信号 */
  311.         I2C_Stop();
  312.         return 0;
  313. }

  314. /**
  315.   * 函数功能: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
  316.   * 输入参数: WriteBuf : 存放带写入的数据的缓冲区指针
  317.   *           Address : 起始地址
  318.   *           Size : 数据长度,单位为字节
  319.   * 返 回 值:  0 表示失败,1表示成功
  320.   * 说    明:无
  321.   */
  322. uint8_t EEPROM_WriteBytes(uint8_t *WriteBuf, uint16_t Address, uint16_t Size)
  323. {
  324.         uint16_t i,m;
  325.         uint16_t usAddr;
  326.         
  327.         /*
  328.          * 写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
  329.          * 对于24xx02,page size = 8
  330.          * 简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址
  331.          * 为了提高连续写的效率: 本函数采用page wirte操作。
  332.          */

  333.         usAddr = Address;        
  334.         for (i = 0; i < Size; i++)
  335.         {
  336.                 /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
  337.                 if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
  338.                 {
  339.                         /* 第0步:发停止信号,启动内部写操作 */
  340.                         I2C_Stop();
  341.                         
  342.                         /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms                        
  343.                                 CLK频率为200KHz时,查询次数为30次左右
  344.                         */
  345.                         for (m = 0; m < 1000; m++)
  346.                         {                                
  347.                                 /* 第1步:发起I2C总线启动信号 */
  348.                                 I2C_Start();
  349.                                 
  350.                                 /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  351.                                 I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);        /* 此处是写指令 */
  352.                                 
  353.                                 /* 第3步:发送一个时钟,判断器件是否正确应答 */
  354.                                 if (I2C_WaitAck() == 0)
  355.                                 {
  356.                                         break;
  357.                                 }
  358.                         }
  359.                         if (m  == 1000)
  360.                         {
  361.                                 goto cmd_fail;        /* EEPROM器件写超时 */
  362.                         }
  363.                
  364.                         /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
  365.                         I2C_SendByte((uint8_t)usAddr);
  366.                         
  367.                         /* 第5步:等待ACK */
  368.                         if (I2C_WaitAck() != 0)
  369.                         {
  370.                                 goto cmd_fail;        /* EEPROM器件无应答 */
  371.                         }
  372.                 }
  373.         
  374.                 /* 第6步:开始写入数据 */
  375.                 I2C_SendByte(WriteBuf[i]);
  376.         
  377.                 /* 第7步:发送ACK */
  378.                 if (I2C_WaitAck() != 0)
  379.                 {
  380.                         goto cmd_fail;        /* EEPROM器件无应答 */
  381.                 }

  382.                 usAddr++;        /* 地址增1 */               
  383.         }
  384.         
  385.         /* 命令执行成功,发送I2C总线停止信号 */
  386.         I2C_Stop();
  387.         return 1;

  388. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  389.         /* 发送I2C总线停止信号 */
  390.         I2C_Stop();
  391.         return 0;
  392. }

  393. /******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/

复制代码




谢谢分享!

关注小编的例子, 很不错啊

非常感谢,专门登陆来感谢

非常感谢,但是,,,,,,,,;链接挂了```

百度云盘:https://pan.baidu.com/s/1slN8rIt 密码:u6m1

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top