一 概念
- 是什么? 两根同步串行总线(数据线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传输过程
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)
代码实现
来自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);
}