I2C学习24C64

https://blog.csdn.net/hzb15195948039/article/details/86673431?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EESLANDING%7Edefault-2-86673431-blog-126924091.pc_relevant_landingrelevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EESLANDING%7Edefault-2-86673431-blog-126924091.pc_relevant_landingrelevant&utm_relevant_index=5

 

前言:

常用的IIC类存储设备是以EEPROM和铁电为主。如:AT24C0xBN(x=2,4,8),这是ATMEL公司的EEPROM;FM24CL64B或者FM24CL16B,这是CYPRESS公司的铁电(F-RAM)。后续章节IIC程序是基于AT24C04BN和FM24CL64B这两款设备。

IIC时序简介:

起始信号:在SCL为高电平期间,SDA由高变低;

停止信号:在SCL为高电平期间,SDA由低变高。如下图所示:

应答信号:在8位数据信号传输完成之后,第9位就是应答信号。应答信号分为应答和非应答,应答为低电平,非应答为高电平,如下图所示:

IIC总线数据传输时序如下:

对于AT24C04BN与FM24CL64B在读写操作时,存在点差别。主要是因为FM24CL64B的内存64KB,寻址空间是0x0000~0xFFFFH,访问时需要先传输地址高字节,再传输地址低字节。

AT24C04BN写数据流程:START-->DEVICE ADDRESS(写)-->ACK-->WORD ADDRESS-->ACK-->DATA-->ACK-->STOP,其中ACK为从设备发送的应答信号(低电平),主机接收应答。

AT24C04BN读数据流程:START-->DEVICE ADDRESS(写)-->ACK-->WORD ADDRESS-->START-->DEVICE ADDRESS(读)-->ACK-->DATA-->NACK-->STOP。其中ACK是从设备发送的应答信号,低电平有效,NACK是主机发送的非应答信号,高电平有效。

FM24CL64B写数据流程:START-->Slave Address(写)-->ACK-->Address MSB-->ACK-->Address LSB-->ACK-->Data Byte-->ACK-->STOP。其中ACK是从机发送的应答信号,低电平有效。

FM24CL64B读数据流程:START-->Slave Address(写)-->ACK-->Address MSB-->ACK-->Address LSB-->ACK-->START-->Slave Address(读)-->ACK-->Data Byte-->NACK-->STOP。其中,ACK是从设备发送的应答信号,低电平有效,NACK是主机发送的非应答信号,高电平有效。

试验中,我使用单片机是STM32F091VC,程序中通过宏定义来兼容AT24C04BN和FM24CL64B两款I2C设备。#if 0默认使用AT24C04BN,#if 1则使用FM24CL64B。

#if 1

#define FM24CL64        1

#endif

以下是bsp_fram.h头文件代码:

    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef _BSP_FRAM_H
    #define    _BSP_FRAM_H
     
    #include "main.h"
     
    #ifdef __cplusplus
    extern "C" {
    #endif
    /* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
    /* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
    #define GPIO_PORT_I2C   GPIOB                   /* GPIO端口 */
    #define RCC_I2C_PORT     RCC_AHBPeriph_GPIOB     /* GPIO端口时钟 */
    #define I2C_SCL_PIN        GPIO_Pin_8                 /* 连接到SCL时钟线的GPIO */
    #define I2C_SDA_PIN     GPIO_Pin_9                 /* 连接到SDA数据线的GPIO */
        
    #define I2C_SCL_H    GPIO_SetBits(GPIO_PORT_I2C, I2C_SCL_PIN)            /* SCL = 1 */
    #define I2C_SCL_L    GPIO_ResetBits(GPIO_PORT_I2C, I2C_SCL_PIN)            /* SCL = 0 */
     
    #define I2C_SDA_H    GPIO_SetBits(GPIO_PORT_I2C, I2C_SDA_PIN)            /* SDA = 1 */
    #define I2C_SDA_L    GPIO_ResetBits(GPIO_PORT_I2C, I2C_SDA_PIN)            /* SDA = 0 */
     
    #define I2C_SDA_READ()    GPIO_ReadInputDataBit(GPIO_PORT_I2C, I2C_SDA_PIN)/* 读SDA口线状态 */
        
    #define I2C_SPEED_1K        5000    //根据处理器速度设置,这里处理器速度是72MHz
        
     
    typedef enum
    {
        I2C_SUCCESS = 0,
        I2C_TIMEOUT,
        I2C_ERROR,
    }I2C_StatusTypeDef;
     
    extern uint32_t i2c_speed;    //I2C访问速度 = I2C_SPEED_1K / i2c_speed
     
    /* ---------------------------依照I2C协议编写的时序函数------------------------------*/
    void BSP_I2C_Init(void);                //初始化I2C的IO口                
    void I2C_Start(void);                //发送I2C开始信号
    void I2C_Stop(void);                //发送I2C停止信号
    uint8_t I2C_Wait_ACK(void);    //I2C等待ACK信号
    void I2C_ACK(void);                    //I2C发送ACK信号
    void I2C_NACK(void);                //I2C不发送ACK信号
    void I2C_Send_Byte(uint8_t data);        //I2C发送一个字节
    uint8_t I2C_Read_Byte(uint8_t ack);    //I2C读取一个字节
    uint16_t I2C_SetSpeed(uint16_t speed);//设置I2C速度(1Kbps~400Kbps,speed单位,Kbps)
     
    /* ---------------------------以下部分是封装好的I2C读写函数--------------------------- */
     
    //具体到某一个器件,请仔细阅读器件规格书关于I2C部分的说明,因为某些器件在I2C的读写操作会
    //有一些差异,下面的代码我们在绝大多数的I2C器件中,都是验证OK的!
    I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data);//向I2C从设备写入一个字节
    I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num);//向I2C从设备连续写入Num个字节
    I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data);//从I2C从设备读取一个字节
    I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num);//从I2C设备连续读取Num个字节
    I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet);
     
    #endif /* _bsp_FRAM_H */

以下是bsp_fram.c的c文件代码:

    /* Includes ------------------------------------------------------------------*/
    #include "stm32f0xx.h"
    #include "main.h"
    /* Private typedef -----------------------------------------------------------*/
    /* Private define ------------------------------------------------------------*/
    #if 1
     
    #define    FM24CL64    1
     
    #endif
    /* Private macro -------------------------------------------------------------*/
    /* Private variables ---------------------------------------------------------*/
    // I2C访问速度 = I2C_SPEED_1K / i2c_speed
    uint32_t i2c_speed;
    /* Private function prototypes -----------------------------------------------*/
    /* Private functions ---------------------------------------------------------*/
     
     
    /**
      * @brief  模拟I2C接口初始化
      * @param  None
      * @retval None
      * @note
        *        SCL:     PB8
        *        SDA:    PB9
      */
    void BSP_I2C_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        
        RCC_AHBPeriphClockCmd(RCC_I2C_PORT|RCC_AHBPeriph_GPIOD|RCC_AHBPeriph_GPIOE,ENABLE);        /* 打开GPIO时钟 */
        
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
          GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;   /* 输出 */
        
        
        GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN|I2C_SDA_PIN;
        GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
        
        
        I2C_SetSpeed(100);//设置I2C访问速度为100Kbps
    }
     
    void SDA_IN(void)
    {
          GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_StructInit(&GPIO_InitStructure);  
          GPIO_InitStructure.GPIO_Pin   = I2C_SDA_PIN;  
          GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;
          GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
          GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
    }
    void SDA_OUT(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_StructInit(&GPIO_InitStructure);  
          GPIO_InitStructure.GPIO_Pin   = I2C_SDA_PIN;  
          GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
          GPIO_InitStructure.GPIO_OType  = GPIO_OType_PP;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
          GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
    }
     
     
    /**
        * @brief 产生I2C起始信号
      * @param  None
      * @retval None
      * @note
        *        请参考I2C通信协议,I2C起始信号:当SCL为高电平时,SDA由高变低
        *        如下图所示:方框部分表示I2C起始信号
        *           _____     |
        *        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___  
        *   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     /
        *          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\___/
        *        __|_____|_   |   _    _    _    _    _    _    _    _    _
        *   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_
        *          |_____|    |
        *           start         D7   D6   D5   D4   D3   D2   D1   D0   ACK
        */
    void I2C_Start(void)
    {
        uint32_t i2c_delay = i2c_speed;
        
        SDA_OUT();    //SDA设置为输出
        I2C_SDA_H;    //SDA: 高
        I2C_SCL_H;    //SCL: 高
        i2c_delay = i2c_speed;//延时>4.7us
        while(i2c_delay--){}
         I2C_SDA_L;    //当SCL为高电平时,SDA由高变低
        i2c_delay = i2c_speed;//延时>4us
        while(i2c_delay--){}
        I2C_SCL_L;    //SCL变低,钳住I2C总线,准备发送或接收数据
    }
     
    /**
        * @brief 产生I2C停止信号
      * @param  None
      * @retval None
      * @note
        *        请参考I2C通信协议,I2C停止信号:当SCL为高电平时,SDA由低变高
        *        发送完STOP信号后,SCL和SDA都为高电平,即释放了I2C总线
        *        如下图所示:方框部分表示I2C起始信号
        *                                    _____
        *         ___  ___  ___  ___        |   __|_
        *   SDA: /   \/   \/   \/   \       |  /  |
        *        \___/\___/\___/\___/\______|_/   |
        *         _    _    _    _    _    _|_____|_
        *   SCL: / \__/ \__/ \__/ \__/ \__/ |     |
        *                                   |_____|
        *        D3   D2   D1   D0   ACK     stop
        */
    void I2C_Stop(void)
    {
        uint32_t i2c_delay = i2c_speed;
        
        SDA_OUT();         //SDA设置为输出
        I2C_SDA_L;    //SDA低电平
        I2C_SCL_H;    //SCL高电平
         i2c_delay = i2c_speed;//延时>4us
        while(i2c_delay--){}
        I2C_SDA_H;    //STOP:当SCL为高电平时,SDA由低变高
        i2c_delay = i2c_speed;
        while(i2c_delay--){}//延时>4.7us                               
    }
     
    /**
        * @brief  等待ACK应答信号
      * @param  None
      * @retval 1 - 未接收到应答信号ACK;0 - 接收到应答信号ACK
      * @note
        *        请参考I2C通信协议,检测ACK应答信号:当SCL为高电平时,读取SDA为低电平
        *        如下图所示:方框部分表示I2C起始信号
        *                             ________     _____
        *         ___  ___  ___  ___ | _      |   |   __|_
        *   SDA: /   \/   \/   \/   \|/ \     |   |  /  |
        *        \___/\___/\___/\___/|   \____|___|_/   |
        *         _    _    _    _   | _____  |  _|_____|
        *   SCL: / \__/ \__/ \__/ \__|/     \_|_/ |     |
        *                            |________|   |_____|
        *        D3   D2   D1   D0      ACK        stop
        */
    uint8_t I2C_Wait_ACK(void)
    {
        uint32_t i2c_delay = i2c_speed;
        uint8_t timeout = 0;
        
        SDA_IN();            //SDA设置为输入
        I2C_SDA_H;    //SDA上拉输入
        I2C_SCL_H;    //SCL设置为高电平
        i2c_delay = i2c_speed;
        while(i2c_delay--){}
        while(I2C_SDA_READ() == 1)//等待ACK
        {
            if(timeout++ > 250)
            {
                I2C_Stop();
                return 1;
            }
        }
        I2C_SCL_L;//钳住I2C总线:时钟信号设为低电平
        return 0;  
    }
     
     
    /**
        * @brief  产生ACK应答信号
      * @param  None
      * @retval None
      * @note
        *        请参考I2C通信协议,产生ACK应答信号: 在SDA为低电平时,SCL产生一个正脉冲
        *        如下图所示:方框部分表示I2C起始信号
        *                             _____     _____
        *         ___  ___  ___  ___ |     |   |   __|_
        *   SDA: /   \/   \/   \/   \|     |   |  /  |
        *        \___/\___/\___/\___/|\____|___|_/   |
        *         _    _    _    _   |  _  |  _|_____|_
        *   SCL: / \__/ \__/ \__/ \__|_/ \_|_/ |     |
        *                            |_____|   |_____|
        *        D3   D2   D1   D0     ACK      stop
        */
    void I2C_ACK(void)
    {
        uint32_t i2c_delay = i2c_speed;
        
        I2C_SCL_L;    //低电平
        SDA_OUT();        //设置SDA为输出
        I2C_SDA_L;    //ACK信号
        i2c_delay = i2c_speed;
        while(i2c_delay--){}//延时>4us
        I2C_SCL_H;    //高电平
        i2c_delay = i2c_speed;
        while(i2c_delay--){}//延时>4us
        I2C_SCL_L;    //钳住I2C总线:时钟信号设为低电平
    }
     
     
    /**
        * @brief  产生非应答信号NACK
      * @param  None
      * @retval None
      * @note
        *        请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲
        *        如下图所示:方框部分表示I2C起始信号
        *                             _____      ______
        *         ___  ___  ___  ___ | ____|_   |    __|_
        *   SDA: /   \/   \/   \/   \|/    | \  |   /  |
        *        \___/\___/\___/\___/|     |  \_|__/   |
        *         _    _    _    _   |  _  |  __|______|_
        *   SCL: / \__/ \__/ \__/ \__|_/ \_|_/  |      |
        *                            |_____|    |______|
        *        D3   D2   D1   D0    NACK        stop
        */        
    void I2C_NACK(void)
    {
        uint32_t i2c_delay = i2c_speed;
        
        I2C_SCL_L;    //低电平
        SDA_OUT();        //SDA设置为输出
        I2C_SDA_H;    //NACK信号
        i2c_delay = i2c_speed;
        while(i2c_delay--){}//延时>4us
        I2C_SCL_H;    //高电平
        i2c_delay = i2c_speed;
        while(i2c_delay--){}//延时>4us
        I2C_SCL_L;    //钳住I2C总线:时钟信号设为低电平
    }
     
     
    /**
        * @brief  I2C发送一个字节
      * @param  None
      * @retval None
      * @note
        *        请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲
        *        如下图所示:方框部分表示I2C起始信号
        *
        *           _____     |<------------I2C数据发送周期------------>|
        *        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___ | _
        *   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \|/
        *          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/|\_
        *        __|_____|_   |   _    _    _    _    _    _    _    _  |  
        *   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_
        *          |_____|    |                                         |
        *           start     |   D7   D6   D5   D4   D3   D2   D1   D0 |
        */
    void I2C_Send_Byte(uint8_t data)
    {                        
        uint8_t i = 0;
        uint32_t i2c_delay = i2c_speed;
     
        SDA_OUT();                                    //SDA设为输出
        I2C_SCL_L;                                //钳住I2C总线:SCL设为低电平
        for(i = 0; i < 8; i++)
        {
            if(data&0x80)I2C_SDA_H;    //高位先传
            else I2C_SDA_L;
            
            i2c_delay = i2c_speed;
            while(i2c_delay--){}            //延时>4us
        
            I2C_SCL_H;                            //在SCL上产生一个正脉冲
            i2c_delay = i2c_speed;
            while(i2c_delay--){}            //延时>4us
                
            I2C_SCL_L;
            i2c_delay = i2c_speed/3;
            while(i2c_delay--){}            //延时>1us
            data <<= 1;                                //右移一位
        }
    }
     
     
    /**
        * @brief  从I2C读取一个字节
      * @param  ack : 0 - NACK; 1 - ACK
      * @retval 接收到的数据
      * @note
        *        请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲
        *        如下图所示:方框部分表示I2C起始信号
        *
        *           _____     |<------------I2C数据读取周期(ACK)------------>|
        *        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___      |
        *   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \     |
        *          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/\____|_
        *        __|_____|_   |   _    _    _    _    _    _    _    _    _  |
        *   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_
        *          |_____|    |                                              |
        *           start     |   D7   D6   D5   D4   D3   D2   D1   D0   ACK
        *
        *           _____     |<------------I2C数据读取周期(NACK)----------->|
        *        __|__   |    |  ___  ___  ___  ___  ___  ___  ___  ___  ____|_
        *   SDA:   |  \__|____|_/   \/   \/   \/   \/   \/   \/   \/   \/    |
        *          |     |    | \___/\___/\___/\___/\___/\___/\___/\___/     |
        *        __|_____|_   |   _    _    _    _    _    _    _    _    _  |
        *   SCL:   |     | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_
        *          |_____|    |                                              |
        *           start     |   D7   D6   D5   D4   D3   D2   D1   D0  NACK
        */
    uint8_t I2C_Read_Byte(uint8_t ack)
    {
        uint8_t i, receive = 0x00;
        uint32_t i2c_delay = i2c_speed;
        
        I2C_SCL_L;                                    //SCL低电平
        SDA_IN();                                            //SDA设置为输入
        for(i = 0; i < 8; i++)
        {
            i2c_delay = i2c_speed;
            while(i2c_delay--);
            I2C_SCL_H;                                //高电平
            i2c_delay = i2c_speed;
            while(i2c_delay--);
            receive <<= 1;
            if(I2C_SDA_READ()) receive |= 1;    //高位在前
            I2C_SCL_L;
      }
        if (ack == 0) I2C_NACK();            //发送NACK
        else I2C_ACK();                                //发送ACK
        
        return receive;                                //返回接收到的数据
    }
     
    /**
      * @brief  设置I2C速度
      * @param  speed : I2C速度,单位Kbps
      * @retval 返回设置前的I2C速度
        * @note   I2C速度设置范围是: 1Kbps ~ 400Kbps
      */
    uint16_t I2C_SetSpeed(uint16_t speed)
    {
        uint16_t temp;
        
        //I2C速度必须小于400Kbps,大于 1Kbps
        if((speed > 400)|| (speed < 1)) return 0;
        
        temp = I2C_SPEED_1K / i2c_speed;    //备份原来的i2c速度
        i2c_speed = I2C_SPEED_1K / speed;    //设置新的i2c速度
     
        return temp;    //返回设置前的i2c速度
    }
     
    /* ---------------------------以下部分是封装好的I2C读写函数--------------------------- */
     
    //具体到某一个器件,请仔细阅读器件规格书关于I2C部分的说明,因为某些器件I2C的读写操作会
    //有一些差异,下面的代码我们在绝大多数的I2C器件中,都是验证OK的!
     
    /**
      * @brief  向设备指定地址写入单一Byte数据
      * @param  DevAddr : I2C从设备地址
      * @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)
      * @param  Data    : 写入的数据
      * @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR
        * @note   
        *   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写
        *        2 - 时序:
        *           _______________________________________
        *          | |         |   |        |   |    |   | |
        *   Master:|S|DevAddr+W|   |DataAddr|   |Data|   |P|
        *          |_|_________|___|________|___|____|___|_|
        *           _______________________________________
        *          | |         |   |        |   |    |   | |
        *   Slave: | |         |ACK|        |ACK|    |ACK| |
        *          |_|_________|___|________|___|____|___|_|
      */
    I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data)
    {
        I2C_Start();                          //Master发送起始信号
        I2C_Send_Byte(DevAddr);                  //Master发送从设备地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #ifdef FM24CL64
        I2C_Send_Byte(DataAddr >> 8);          //发送高8位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #else
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #endif
        I2C_Send_Byte(Data);                                    //发送数据
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        I2C_Stop();                                                        //发送停止信号
        return I2C_SUCCESS;
    }
     
    /**
      * @brief  向设备指定地址连续写入数据(Burst写模式)
      * @param  DevAddr : I2C从设备地址
      * @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)
        *                   对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址
      * @param  *pData  : 写入的数据首地址
      * @param     Num  : 连续写入的数据个数
      * @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR
        * @note   
        *   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写
        *        2 - 时序:
        *           ____________________________________________________
        *          | |         |   |        |   |    |   |   |    |   | |
        *   Master:|S|DevAddr+W|   |DataAddr|   |Data|   |...|Data|   |P|
        *          |_|_________|___|________|___|____|___|___|____|___|_|
        *           ____________________________________________________
        *          | |         |   |        |   |    |   |   |    |   | |
        *   Slave: | |         |ACK|        |ACK|    |ACK|...|    |ACK| |
        *          |_|_________|___|________|___|____|___|___|____|___|_|
      */
    I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
    {
        uint32_t i = 0;
        
        I2C_Start();                              //Master发送起始信号
        I2C_Send_Byte(DevAddr);                      //Master发送从设备地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;      //等待ACK超时错误
    #ifdef FM24CL64
        I2C_Send_Byte(DataAddr >> 8);          //发送高8位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #else
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #endif
        for(i = 0; i < Num; i++)
        {
            I2C_Send_Byte(*(pData+i));              //发送数据
            if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        }
        I2C_Stop();    //发送停止信号
        return I2C_SUCCESS;
    }
     
    /**
      * @brief  从指定设备读取1Byte数据
      * @param  DevAddr : I2C从设备地址
      * @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)
      * @param  *Data   : 数据的存放地址
      * @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR
        * @note   
        *   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写
        *        2 - 时序:
        *           _________________________________________________________
        *          | |         |   |        |    | |         |   |    |    | |
        *   Master:|S|DevAddr+W|   |DataAddr|    |S|DevAddr+R|   |    |NACK|P|
        *          |_|_________|___|________|____|_|_________|___|____|____|_|
        *           _________________________________________________________
        *          | |         |   |        |    | |         |   |    |    | |
        *   Slave: | |         |ACK|        |ACK | |         |ACK|Data|    | |
        *          |_|_________|___|________|____|_|_________|___|____|____|_|
      */
    I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data)
    {
        I2C_Start();                            //Master发送起始信号
        I2C_Send_Byte(DevAddr);                    //Master发送从设备地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;    //等待ACK超时错误
    #ifdef FM24CL64
        I2C_Send_Byte(DataAddr >> 8);          //发送高8位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #else
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #endif
        
        I2C_Start();                                                    //Master发送起始信号
        I2C_Send_Byte(DevAddr+1);                            //Master发送从设备读地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        *Data = I2C_Read_Byte(0);                            //读数据,NACK
        I2C_Stop();                                                        //发送停止信号
        return I2C_SUCCESS;
    }
     
    /**
      * @brief  向设备指定地址连续写入数据(Burst写模式)
      * @param  DevAddr : I2C从设备地址
      * @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)
        *                   对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址
      * @param  *pData  : 写入的数据首地址
      * @param     Num  : 连续写入的数据个数
      * @retval    I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR
        * @note   
        *   1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写
        *        2 - 时序:
        *           _____________________________________________________________________
        *          | |         |   |        |   | |         |   |    |   |   |    |    | |
        *   Master:|S|DevAddr+W|   |DataAddr|   |S|DevAddr+R|   |    |ACK|...|    |NACK|P|
        *          |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|
        *           _____________________________________________________________________
        *          | |         |   |        |   | |         |   |    |   |   |    |    | |
        *   Slave: | |         |ACK|        |ACK| |         |ACK|Data|   |...|Data|    | |
        *          |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|
      */
    I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
    {
        uint32_t i = 0;
        
        I2C_Start();                          //Master发送起始信号
        I2C_Send_Byte(DevAddr);                  //Master发送从设备地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #ifdef FM24CL64
        I2C_Send_Byte(DataAddr >> 8);          //发送高8位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #else
        I2C_Send_Byte(DataAddr);              //发送低八位数据地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
    #endif
        
        I2C_Start();                          //Master发送起始信号
        I2C_Send_Byte(DevAddr+1);              //Master发送从设备读地址
        if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
        
        for(i = 0; i < (Num-1); i++)
        {
            *(pData+i) = I2C_Read_Byte(1);    //读数据,ACK
        }
        *(pData+i) = I2C_Read_Byte(0);          //读数据,NACK
        
        I2C_Stop();                              //发送停止信号
        return I2C_SUCCESS;
    }
     
     
    /**
      * @brief  设置数据的某一位
      * @param  DevAddr : I2C从设备地址
      * @param  DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)
      * @param  Bitx  : 第几位
      * @param  BitSet: 需要设置的值
      * @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR
        * @note  
        */
    I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet)
    {
        I2C_StatusTypeDef status = I2C_ERROR;
        uint8_t tempdata = 0;
        
        status = I2C_ReadOneByte(DevAddr, DataAddr, &tempdata);    //获取原有数据
        if(status != I2C_SUCCESS) return status;                //I2C错误,则返回
        
        tempdata &= ~(1<<Bitx);                                    //将要设定的位清零
        tempdata |= (BitSet<<Bitx);                                //设置指定的bit
        status = I2C_WriteOneByte(DevAddr, DataAddr, tempdata);    //写入数据
        
        return status;    //返回状态
    }
————————————————
版权声明:本文为CSDN博主「劲风草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hzb15195948039/article/details/86673431

posted @ 2022-11-04 12:45  Kconfig  阅读(1003)  评论(0编辑  收藏  举报