打印
[STM32F1]

【HAL库每天一例】第050例:I2C-EEPROM

[复制链接]
1889|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
亼亽|  楼主 | 2016-6-24 08:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
【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文件内容
#ifndef __I2C_EEPROM_H__
#define        __I2C_EEPROM_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

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

#define EEPROM_I2Cx                                I2C1
#define EEPROM_I2C_RCC_CLK_ENABLE()                __HAL_RCC_I2C1_CLK_ENABLE()
#define EEPROM_I2C_RCC_CLK_DISABLE()               __HAL_RCC_I2C1_CLK_DISABLE()

#define EEPROM_I2C_GPIO_CLK_ENABLE()               __HAL_RCC_GPIOB_CLK_ENABLE()
#define EEPROM_I2C_GPIO_CLK_DISABLE()              __HAL_RCC_GPIOB_CLK_DISABLE()   
#define EEPROM_I2C_GPIO_PORT                       GPIOB   
#define EEPROM_I2C_SCL_PIN                         GPIO_PIN_6
#define EEPROM_I2C_SDA_PIN                         GPIO_PIN_7

/*
* EEPROM 2kb = 2048bit = 2048/8 B = 256 B
* 32 pages of 8 bytes each
*
* Device Address
* 1 0 1 0 A2 A1 A0 R/W
* 1 0 1 0 0  0  0  0 = 0XA0
* 1 0 1 0 0  0  0  1 = 0XA1
*/
/* EEPROM Addresses defines */
#define EEPROM_I2C_ADDRESS                         0xA0

/* 扩展变量 ------------------------------------------------------------------*/
extern I2C_HandleTypeDef hi2c_eeprom;

/* 函数声明 ------------------------------------------------------------------*/
void               MX_I2C_EEPROM_Init(void);
void               I2C_EEPROM_WriteData(uint16_t Addr, uint8_t Reg, uint8_t Value);
HAL_StatusTypeDef  I2C_EEPROM_WriteBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length);
uint8_t            I2C_EEPROM_ReadData(uint16_t Addr, uint8_t Reg);
HAL_StatusTypeDef  I2C_EEPROM_ReadBuffer(uint16_t Addr, uint8_t Reg, uint16_t RegSize, uint8_t *pBuffer, uint16_t Length);
HAL_StatusTypeDef  I2C_EEPROM_IsDeviceReady(uint16_t DevAddress, uint32_t Trials);

#endif /* __I2C_EEPROM_H__ */

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

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

/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
#define EVAL_I2Cx_TIMEOUT_MAX                   3000

/* 私有变量 ------------------------------------------------------------------*/
I2C_HandleTypeDef hi2c_eeprom;
uint32_t I2cxTimeout = EVAL_I2Cx_TIMEOUT_MAX;

/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: I2C外设初始化
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void MX_I2C_EEPROM_Init(void)
{
  hi2c_eeprom.Instance             = EEPROM_I2Cx;
  hi2c_eeprom.Init.ClockSpeed      = I2C_SPEEDCLOCK;
  hi2c_eeprom.Init.DutyCycle       = I2C_DUTYCYCLE;
  hi2c_eeprom.Init.OwnAddress1     = 0;
  hi2c_eeprom.Init.AddressingMode  = I2C_ADDRESSINGMODE_7BIT;
  hi2c_eeprom.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c_eeprom.Init.OwnAddress2     = 0;
  hi2c_eeprom.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c_eeprom.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;
  HAL_I2C_Init(&hi2c_eeprom);
}

/**
  * 函数功能: I2C外设硬件初始化配置
  * 输入参数: hi2c:I2C句柄类型指针
  * 返 回 值: 无
  * 说    明: 该函数被HAL库内部调用
  */
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==EEPROM_I2Cx)
  {  
    /* 使能外设时钟 */
    EEPROM_I2C_RCC_CLK_ENABLE();        
    EEPROM_I2C_GPIO_CLK_ENABLE();
   
    /**I2C1 GPIO Configuration   
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA
    */
    GPIO_InitStruct.Pin = EEPROM_I2C_SCL_PIN|EEPROM_I2C_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(EEPROM_I2C_GPIO_PORT, &GPIO_InitStruct);
  }
}

/**
  * 函数功能: I2C外设硬件反初始化配置
  * 输入参数: hi2c:I2C句柄类型指针
  * 返 回 值: 无
  * 说    明: 该函数被HAL库内部调用
  */
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
{
  if(hi2c->Instance==EEPROM_I2Cx)
  {
    /* 禁用外设时钟 */
    EEPROM_I2C_GPIO_CLK_DISABLE();
  
    /**I2C1 GPIO Configuration   
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA
    */
    HAL_GPIO_DeInit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN|EEPROM_I2C_SDA_PIN);
  }
}


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

/**
  * 函数功能: 通过I2C写入一个值到指定寄存器内
  * 输入参数: Addr:I2C设备地址
  *           Reg:目标寄存器
  *           Value:值
  * 返 回 值: 无
  * 说    明: 无
  */
void I2C_EEPROM_WriteData(uint16_t Addr, uint8_t Reg, uint8_t Value)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  status = HAL_I2C_Mem_Write(&hi2c_eeprom, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1, I2cxTimeout);
  
  /* 检测I2C通信状态 */
  if(status != HAL_OK)
  {
    /* 调用I2C通信错误处理函数 */
    I2C_EEPROM_Error();
  }
}

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

  /* 检测I2C通信状态 */
  if(status != HAL_OK)
  {
    /* 调用I2C通信错误处理函数 */
    I2C_EEPROM_Error();
  }        
  return status;
}


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

  /* 检测I2C通信状态 */
  if(status != HAL_OK)
  {
    /* 调用I2C通信错误处理函数 */
    I2C_EEPROM_Error();
  
  }
  return value;
}

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

  status = HAL_I2C_Mem_Read(&hi2c_eeprom, Addr, (uint16_t)Reg, RegSize, pBuffer, Length, I2cxTimeout);
  
  /* 检测I2C通信状态 */
  if(status != HAL_OK)
  {
    /* 调用I2C通信错误处理函数 */
    I2C_EEPROM_Error();
  }        
  return status;
}

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

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



沙发
亼亽|  楼主 | 2016-6-24 08:28 | 只看该作者
2. EEPROM(AT24C02)-软件模拟I2C(建议使用)
bsp_EEPROM.h文件内容
#ifndef __I2C_EEPROM_H__
#define        __I2C_EEPROM_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/
#define I2C_OWN_ADDRESS                            0x0A

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

#define I2C_GPIO_CLK_ENABLE()               __HAL_RCC_GPIOB_CLK_ENABLE()
#define I2C_GPIO_PORT                       GPIOB   
#define I2C_SCL_PIN                         GPIO_PIN_6
#define I2C_SDA_PIN                         GPIO_PIN_7

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

/*
* AT24C02 2kb = 2048bit = 2048/8 B = 256 B
* 32 pages of 8 bytes each
*
* Device Address
* 1 0 1 0 A2 A1 A0 R/W
* 1 0 1 0 0  0  0  0 = 0XA0
* 1 0 1 0 0  0  0  1 = 0XA1
*/

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

/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void    I2C_Start(void);
void    I2C_Stop(void);
void    I2C_SendByte(uint8_t _ucByte);
uint8_t I2C_ReadByte(void);
uint8_t I2C_WaitAck(void);
void    I2C_Ack(void);
void    I2C_NAck(void);
uint8_t I2C_CheckDevice(uint8_t _Address);

uint8_t EEPROM_CheckOk(void);
uint8_t EEPROM_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);
uint8_t EEPROM_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);



#endif /* __I2C_EEPROM_H__ */

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

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

/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: I2C总线位延迟,最快400KHz
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
static void I2C_Delay(void)
{
        uint8_t i;

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

/**
  * 函数功能: CPU发起I2C总线启动信号
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void I2C_Start(void)
{
        /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
        I2C_SDA_HIGH();
        I2C_SCL_HIGH();
        I2C_Delay();
        I2C_SDA_LOW();
        I2C_Delay();
        I2C_SCL_LOW();
        I2C_Delay();
}

/**
  * 函数功能: CPU发起I2C总线停止信号
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void I2C_Stop(void)
{
        /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
        I2C_SDA_LOW();
        I2C_SCL_HIGH();
        I2C_Delay();
        I2C_SDA_HIGH();
}

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

        /* 先发送字节的高位bit7 */
        for (i = 0; i < 8; i++)
        {               
                if (Byte & 0x80)
                {
                        I2C_SDA_HIGH();
                }
                else
                {
                        I2C_SDA_LOW();
                }
                I2C_Delay();
                I2C_SCL_HIGH();
                I2C_Delay();       
                I2C_SCL_LOW();
                if (i == 7)
                {
                        I2C_SDA_HIGH(); // 释放总线
                }
                Byte <<= 1;        /* 左移一个bit */
                I2C_Delay();
        }
}


/**
  * 函数功能: CPU从I2C总线设备读取8bit数据
  * 输入参数: 无
  * 返 回 值: 读到的数据
  * 说    明:无
  */
uint8_t I2C_ReadByte(void)
{
        uint8_t i;
        uint8_t value;

        /* 读到第1个bit为数据的bit7 */
        value = 0;
        for (i = 0; i < 8; i++)
        {
                value <<= 1;
                I2C_SCL_HIGH();
                I2C_Delay();
                if (I2C_SDA_READ())
                {
                        value++;
                }
                I2C_SCL_LOW();
                I2C_Delay();
        }
        return value;
}

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

        I2C_SDA_HIGH();        /* CPU释放SDA总线 */
        I2C_Delay();
        I2C_SCL_HIGH();        /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
        I2C_Delay();
        if (I2C_SDA_READ())        /* CPU读取SDA口线状态 */
        {
                re = 1;
        }
        else
        {
                re = 0;
        }
        I2C_SCL_LOW();
        I2C_Delay();
        return re;
}

/**
  * 函数功能: CPU产生一个ACK信号
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void I2C_Ack(void)
{
        I2C_SDA_LOW();        /* CPU驱动SDA = 0 */
        I2C_Delay();
        I2C_SCL_HIGH();        /* CPU产生1个时钟 */
        I2C_Delay();
        I2C_SCL_LOW();
        I2C_Delay();
        I2C_SDA_HIGH();        /* CPU释放SDA总线 */
}

/**
  * 函数功能: CPU产生1个NACK信号
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void I2C_NAck(void)
{
        I2C_SDA_HIGH();        /* CPU驱动SDA = 1 */
        I2C_Delay();
        I2C_SCL_HIGH();        /* CPU产生1个时钟 */
        I2C_Delay();
        I2C_SCL_LOW();
        I2C_Delay();       
}

/**
  * 函数功能: 配置I2C总线的GPIO,采用模拟IO的方式实现
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
static void I2C_InitGPIO(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* 打开GPIO时钟 */
        I2C_GPIO_CLK_ENABLE();

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

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

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

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

/**
  * 函数功能: 判断串行EERPOM是否正常
  * 输入参数: 无
  * 返 回 值: 1 表示正常, 0 表示不正常
  * 说    明:无
  */
uint8_t EEPROM_CheckOk(void)
{
        if(I2C_CheckDevice(EEPROM_DEV_ADDR) == 0)
        {
                return 1;
        }
        else
        {
                /* 失败后,切记发送I2C总线停止信号 */
                I2C_Stop();               
                return 0;
        }
}

/**
  * 函数功能: 从串行EEPROM指定地址处开始读取若干数据
  * 输入参数: ReadBuf : 存放读到的数据的缓冲区指针
  *           Address : 起始地址  
  *           Size : 数据长度,单位为字节
  * 返 回 值:  0 表示失败,1表示成功
  * 说    明:无
  */
uint8_t EEPROM_ReadBytes(uint8_t *ReadBuf, uint16_t Address, uint16_t Size)
{
        uint16_t i;
       
        /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
       
        /* 第1步:发起I2C总线启动信号 */
        I2C_Start();       
  
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);        /* 此处是写指令 */       
  
        /* 第3步:等待ACK */
        if (I2C_WaitAck() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
  
        /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
        I2C_SendByte((uint8_t)Address);       
  
        /* 第5步:等待ACK */
        if (I2C_WaitAck() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }       
        /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
        I2C_Start();       
  
        /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD);        /* 此处是读指令 */
       
        /* 第8步:发送ACK */
        if (I2C_WaitAck() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }       
       
        /* 第9步:循环读取数据 */
        for (i = 0; i < Size; i++)
        {
                ReadBuf[i] = I2C_ReadByte();        /* 读1个字节 */
               
                /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
                if (i != Size - 1)
                {
                        I2C_Ack();        /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
                }
                else
                {
                        I2C_NAck();        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
                }
        }
        /* 发送I2C总线停止信号 */
        I2C_Stop();
        return 1;        /* 执行成功 */

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

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

        usAddr = Address;       
        for (i = 0; i < Size; i++)
        {
                /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
                if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
                {
                        /* 第0步:发停止信号,启动内部写操作 */
                        I2C_Stop();
                       
                        /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms                        
                                CLK频率为200KHz时,查询次数为30次左右
                        */
                        for (m = 0; m < 1000; m++)
                        {                               
                                /* 第1步:发起I2C总线启动信号 */
                                I2C_Start();
                               
                                /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
                                I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);        /* 此处是写指令 */
                               
                                /* 第3步:发送一个时钟,判断器件是否正确应答 */
                                if (I2C_WaitAck() == 0)
                                {
                                        break;
                                }
                        }
                        if (m  == 1000)
                        {
                                goto cmd_fail;        /* EEPROM器件写超时 */
                        }
               
                        /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
                        I2C_SendByte((uint8_t)usAddr);
                       
                        /* 第5步:等待ACK */
                        if (I2C_WaitAck() != 0)
                        {
                                goto cmd_fail;        /* EEPROM器件无应答 */
                        }
                }
       
                /* 第6步:开始写入数据 */
                I2C_SendByte(WriteBuf[i]);
       
                /* 第7步:发送ACK */
                if (I2C_WaitAck() != 0)
                {
                        goto cmd_fail;        /* EEPROM器件无应答 */
                }

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

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

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




使用特权

评论回复
板凳
mmuuss586| | 2016-6-24 09:15 | 只看该作者
谢谢分享;

使用特权

评论回复
地板
huangyanga| | 2017-2-17 14:05 | 只看该作者

使用特权

评论回复
5
小白学习嵌入式| | 2020-10-20 10:06 | 只看该作者
楼主 可以看看你的main.c里面的逻辑不?872814366@qq.com

使用特权

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

本版积分规则

122

主题

216

帖子

48

粉丝