【通信协议】I2C通信的概念、组成及代码实现

一 概念

  • 是什么? 两根同步串行总线(数据线SDA与时钟线SCL)
  • 有什么用? 串行地传输数据、控制信号等
  • 怎么用? 利用这两根线,应用了对应的通信的协议,使得两个设备之间可以传输数据。实际应用中I2C协议的实现加上附加整体可以视作I2C模块,可以隐藏内部的实现为外部提供统一的的抽象API。

二 组成

  • 主从设备形式(Mater-Slave),可以有多个主机 + 可以有多个从机
  • SDA (Serial Data) – 数据线(串行传输数据)
  • SCL (Serial Clock) – 时钟线(由主机所控制的时钟同步信号)
  • 传输的数据,按照固定的格式打包(数据帧,Message)

三 特点

  • 简单、高效且短距离传输
  • 半双工通信(两根线都具有收发功能,但在同一时刻只能是发送或者是接收)
  • 主设备通过传输的地址帧来查找通信的从设备
  • 当有多个主设备时,需要对设备进行仲裁
  • 传输速率的模式选择
    • 100 kbit/s in standard mode
    • 400 kbit/s in fast mode
    • 3.4 Mbit/s in high-speed mode

四 实现过程

数据帧组成

START + 7位从设备地址 + 读写标志位 + ACK + 寄存器地址(如果是读取请求的话)+ ACK + 数据(8位)

1. 从机的二进制地址帧,7-bit/10-bit
2. 一个或多个数据帧
3. 开始条件和停止条件
4. 读/写位
5. 数据帧之间的ACK / NACK位:

几种传输数据的数据帧形式

传输开始与停止

  • 开始:当SCL是高电平时,SDA从高电平向低电平切换
  • 结束:当SCL是高电平时,SDA由低电平向高电平切换

时序图

一个完整的I2C传输过程
A Complete I2C Date Transfer

I2C通信的过程描述

  • I2C 根据SCL触发启动/终止/重启条件;
  • 主设备根据地址帧找到从设备,若目标设备地址没有返回ACK,则要么abort要么restart;
  • 主设备陆续传输数据帧,每次输都伴随一次接收方的应答(谁是接收方取决于数据的传输方向)
  • I2C使用SCL来控制数据的传输速率,数据只有在SCL处于高电平时才有效

特殊情况

  • 特殊情况1:对于硬件条件有限的MCU,从设备可以适当减慢主设备发出的低电平时钟,使主设备适应此设备的内部运行速率。
  • 特殊情况2:当有多个master设备加入I2C传输时,SCL的低电平时钟时长按master中最长的算,低电平时长短的master会在低电平时间结束后进入waiting states阶段。多master情况需要对设备进行仲裁
    多主机仲裁的过程
  • 特殊情况3:从地址为10-bit,需要预先发送11110XX + 0 RnW bit + 10-bit address,得到应答后再开始发送(这个视不同的芯片决定,这里参考的Infineon Aurix TC3xx系列的MCU)
    传输10-bit的从设备地址模式

代码实现

来自Aurix TC3xx系列的iLLD库,只摘取了部分相关的代码

I2C的各种属性

  • 地址模式,总线状态,速率模式,传输状态
typedef enum
{
    IfxI2c_AddressMode_7Bit  = 0, /**< \brief sets 7 bit address */
    IfxI2c_AddressMode_10Bit = 1  /**< \brief sets 10 bit address */
} IfxI2c_AddressMode;

typedef enum
{
    IfxI2c_BusStatus_idle        = 0, /**< \brief idle */
    IfxI2c_BusStatus_started     = 1, /**< \brief started */
    IfxI2c_BusStatus_busyMaster  = 2, /**< \brief busy Master */
    IfxI2c_BusStatus_remoteSlave = 3  /**< \brief remote Slave */
} IfxI2c_BusStatus;

typedef enum
{
    IfxI2c_Mode_StandardAndFast = 0,  /**< \brief Sets Standard and Fast speed mode */
    IfxI2c_Mode_HighSpeed       = 1   /**< \brief Sets HighSpeed Mode */
} IfxI2c_Mode;

typedef enum
{
    IfxI2c_I2c_Status_ok         = 0,  /**< \brief ok */
    IfxI2c_I2c_Status_nak        = 1,  /**< \brief NAK */
    IfxI2c_I2c_Status_al         = 2,  /**< \brief Arbitration Lost */
    IfxI2c_I2c_Status_busNotFree = 3,  /**< \brief bus is not free */
    IfxI2c_I2c_Status_error      = 4   /**< \brief error */
} IfxI2c_I2c_Status;

I2C的各种结构

  • 引脚的构成
typedef struct
{
    IfxI2c_Scl_InOut *scl;
    IfxI2c_Sda_InOut *sda;
    IfxPort_PadDriver padDriver;
} IfxI2c_Pins;

//SCL SDA 的pin脚映射结构
typedef const struct
{
    Ifx_I2C*          module;    /**< \brief Base address */
    IfxPort_Pin       pin;       /**< \brief Port pin */
    Ifx_RxSel         inSelect;  /**< \brief Input multiplexer value */
    IfxPort_OutputIdx outSelect; /**< \brief Port control code */
} IfxI2c_Scl_InOut;
typedef const struct
{
    Ifx_I2C*          module;   /**< \brief Base address */
    IfxPort_Pin       pin;      /**< \brief Port pin */
    Ifx_RxSel         inSelect;  /**< \brief Input multiplexer value */
    IfxPort_OutputIdx outSelect; /**< \brief Port control code */
} IfxI2c_Sda_InOut;
  • 抽象的 (1)I2Chandle与配置信息;(2)I2C设备与配置信息
typedef struct
{
    Ifx_I2C          *i2c;             /**< \brief Module Pointer */
    IfxI2c_BusStatus  busStatus;       /**< \brief Status of the bus */
    IfxI2c_I2c_Status status;          /**< \brief Status of the last bus operation */
    float32           baudrate;        /**< \brief Baudrate */
} IfxI2c_I2c;

/** \brief Structure to configure the Module
 */
typedef struct
{
    Ifx_I2C               *i2c;            /**< \brief Module Pointer */
    float32                baudrate;       /**< \brief Baudrate */
    IFX_CONST IfxI2c_Pins *pins;           /**< \brief Pins */
    IfxI2c_Mode            mode;           /**< \brief Speed Mode */
} IfxI2c_I2c_Config;

/** \brief Structure with slave device data
 */
typedef struct
{
    IfxI2c_I2c        *i2c;                 /**< \brief Module Pionter */
    uint16             deviceAddress;       /**< \brief the slave device's address */
    IfxI2c_AddressMode addressMode;         /**< \brief slave device's address (7 or 10 bits) */
    IfxI2c_Mode        speedMode;           /**< \brief slave device in Standard/Fast or High Speed mode. */
} IfxI2c_I2c_Device;

/** \brief Structure to configure the device's data structure
 */
typedef struct
{
    IfxI2c_I2c        *i2c;                 /**< \brief Module Pointer */
    uint16             deviceAddress;       /**< \brief the slave device's address */
    IfxI2c_AddressMode addressMode;         /**< \brief slave device's address (7 or 10 bits) */
    IfxI2c_Mode        speedMode;           /**< \brief slave device in Standard/Fast or High Speed mode. */
} IfxI2c_I2c_deviceConfig;

I2C模块的功能函数

  • 初始化
/** \brief Fills the config structure with default values
 * \param config Structure to configure the Module
 * \param i2c Module address
 * \return None
 */
IFX_EXTERN void IfxI2c_I2c_initConfig(IfxI2c_I2c_Config *config, Ifx_I2C *i2c);

/** \brief Initializes the device Handler
 * \param i2cDevice I2c device Handler
 * \param i2cDeviceConfig Structure to configure the device's data structure
 * \return None
 *
 * A coding example can be found in \ref IfxLld_I2c_I2c_Usage
 *
 */
IFX_EXTERN void IfxI2c_I2c_initDevice(IfxI2c_I2c_Device *i2cDevice, const IfxI2c_I2c_deviceConfig *i2cDeviceConfig);

/** \brief Fills the config structure of the slave device with default values.
 * \param i2cDeviceConfig Structure to configure the device's data structure
 * \param i2c Handler
 * \return None
 */
IFX_EXTERN void IfxI2c_I2c_initDeviceConfig(IfxI2c_I2c_deviceConfig *i2cDeviceConfig, IfxI2c_I2c *i2c);

/** \brief Initializes the Module
 * \param i2c Handler
 * \param config Configuration structure
 * \return None
 *
 * A coding example can be found in \ref IfxLld_I2c_I2c_Usage
 *
 */
IFX_EXTERN void IfxI2c_I2c_initModule(IfxI2c_I2c *i2c, const IfxI2c_I2c_Config *config);
  • 属性的get与set (只摘了部分)
/**
 * \param i2c pointer to i2c registers
 * \param address device address
 * \return None
 */
IFX_INLINE void IfxI2c_setSlaveDeviceAddress(Ifx_I2C *i2c, uint16 address);

/** \brief returns the baudrate of SCL
 * \param i2c i2c Handler
 */
IFX_EXTERN float32 IfxI2c_I2c_getBaudrate(IfxI2c_I2c *i2c);

/** \brief API to get the resource index of the I2C specified.
 * \param i2c Pointer to the I2C HW module (register memory map)
 * \return Resource index of the I2C
 */
IFX_EXTERN IfxI2c_Index IfxI2c_getIndex(Ifx_I2C *i2c);

  • I2C读与写
/** \brief reads the I2c device
 *
 * A coding example can be found in \ref IfxLld_I2c_I2c_Usage
 *
 */
IFX_EXTERN IfxI2c_I2c_Status IfxI2c_I2c_read(IfxI2c_I2c_Device *i2cDevice, volatile uint8 *data, Ifx_SizeT size);

/** \brief writes to the I2c device
 *
 * A coding example can be found in \ref IfxLld_I2c_I2c_Usage
 *
 */
IFX_EXTERN IfxI2c_I2c_Status IfxI2c_I2c_write(IfxI2c_I2c_Device *i2cDevice, volatile uint8 *data, Ifx_SizeT size);

/** \} */

应用举例

使用I2C读写外部设备EEPROM的数据
  • 调用infineon提供的I2C API可以实现I2C模块的初始化以及外部设备的访问,在主函数可以对应调用这二者。
/*********************************************************************************************************************/
/*-----------------------------------------------------Includes------------------------------------------------------*/
/*********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxI2c_I2c.h"

/*********************************************************************************************************************/
/*------------------------------------------------------Macros-------------------------------------------------------*/
/*********************************************************************************************************************/
/* MCP79411 I2C PINS */
//MCP79411->a real-time clock configuration
#define MCP_SCL_PIN                 IfxI2c0_SCL_P15_4_INOUT     /* SCL PIN                                          */
#define MCP_SDA_PIN                 IfxI2c0_SDA_P15_5_INOUT     /* SDA PIN                                          */
#define I2C_BAUDRATE                400000                      /* 400 kHz baud rate                                */

#define MCP79411_EEPROM_ADDRESS     0x57                        /* 7 bit slave device address for reading from EEPROM
                                                                   of MCP79411 is 0b1010111 which is 0x57.          */
#define ADDRESS_OF_MAC_ADDRESS      0xF2                        /* Location of EUI-48 node address (MAC address)    */
#define LENGTH_OF_ADDRESS           1                           /* Length of address of the register, in which the
                                                                   requested MAC address is stored in bytes         */
#define LENGTH_OF_MAC_ADDRESS       6                           /* Length of the MAC address in bytes               */

/*********************************************************************************************************************/
/*-------------------------------------------------Global variables--------------------------------------------------*/
/*********************************************************************************************************************/
IfxI2c_I2c g_i2cHandle;                                         /* I2C handle, initialized from i2cConfig                                     */
IfxI2c_I2c_Device g_i2cDevEeprom;                               /* I2C Slave device handle to EEPROM of MC79411, initialized from i2cDeviceConfig     */

uint8 g_macAddr[LENGTH_OF_MAC_ADDRESS] = {0, 0, 0, 0, 0, 0};    /* Global parameter for 6-byte EUI-48 MAC address   */

/*********************************************************************************************************************/
/*---------------------------------------------Function Implementations----------------------------------------------*/
/*********************************************************************************************************************/
/* This function initializes the I2C in master mode and configures the MCP79411 (real time clock) as an I2C slave */
void init_I2C_module(void)
{
    //1.from iLLD I2C I2C ifxi2c_i2c.c
    /* Initialize module */
    IfxI2c_I2c_Config i2cConfig;                                    /* Create configuration structure               */
    IfxI2c_I2c_initConfig(&i2cConfig, &MODULE_I2C0);                /* Fill structure with default values and Module
                                                                       address                                      */
    /* I2c pin configuration */
    const IfxI2c_Pins MCP_PINS =
    {
            &MCP_SCL_PIN,                                           /* SCL port pin                                 */
            &MCP_SDA_PIN,                                           /* SDA port pin                                 */
            IfxPort_PadDriver_ttlSpeed1                             /* Pad driver mode                              */
    };
    i2cConfig.pins = &MCP_PINS;                                     /* Configure port pins                          */
    i2cConfig.baudrate = I2C_BAUDRATE;                              /* Configure baud rate with 400kHz              */

    //2.from iLLD I2C STD ifxi2c.c
    IfxI2c_I2c_initModule(&g_i2cHandle, &i2cConfig);                /* Initialize module <g_i2cHandle>                           */

    //3.from iLLD I2C I2C ifxi2c_i2c.c
    /* Initialize device */
    IfxI2c_I2c_deviceConfig i2cDeviceConfig;                        /* Create device configuration                  */
    IfxI2c_I2c_initDeviceConfig(&i2cDeviceConfig, &g_i2cHandle);    /* Fill structure with default values and I2C
                                                                       Handler                                      */
    /* Because it is 7 bit long and bit 0 is R/W bit, the device address has to be shifted by 1 */
    i2cDeviceConfig.deviceAddress = MCP79411_EEPROM_ADDRESS << 1; //slave device address xxxx xxx0
    IfxI2c_I2c_initDevice(&g_i2cDevEeprom, &i2cDeviceConfig);       /* Initialize the I2C device handle <g_i2cDevEeprom>            */
}

/* This function executes the I2C data transfer.
 * The location of the MAC address is transmitted, then the MAC address is received.
 */
void read_ext_device_address(void)
{
    /* Address of 6-byte EUI-48 MAC address location */
    uint8 i2cTxBuffer[LENGTH_OF_ADDRESS] = {ADDRESS_OF_MAC_ADDRESS};

    /* Communication via I2C starts by transmitting the address of the specific I2C slave until the slave
     * is ready and confirms the reception via the acknowledge bit (IfxI2c_I2c_Status_nak = not acknowledge).
     * This procedure is done for both the write and read process.
     */
    from iLLD I2C I2C ifxi2c_i2c.c
    /* Write data<i2cTxBuffer> to device<g_i2cDevEeprom>, i2cTxBuffer is a register addr, storing mac addr */
    while(IfxI2c_I2c_write(&g_i2cDevEeprom, &i2cTxBuffer[0], LENGTH_OF_ADDRESS) == IfxI2c_I2c_Status_nak);
    /* Read the unique MAC address */
    while(IfxI2c_I2c_read(&g_i2cDevEeprom, &g_macAddr[0], LENGTH_OF_MAC_ADDRESS) == IfxI2c_I2c_Status_nak);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值