IIC总线

 一、介绍

        I2C(Inter-Integrated Circuit 集成电路)总线是Philips公司在八十年代初推出的一种串行、半双工的总线, 主要用于近距离、低速的芯片之间的通信;I2C总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步;I2C总线硬件结构简单,简化了PCB布线,降低了系统成本,提高了系统可靠性,因此在各个领域得到了广泛应用。I2C总线是一种多主机总线,连接在 I2C总线上的器件分为主机和从机。主机有权发起和结束一次通信,从机只能被动呼叫;当总线上有多个主机同时启用总线时,I2C也具备冲突检测和仲裁的功能来防止错误产生;每个连接到I2C总线上的器件都有一个唯一的地址(7bit) ,传输数据的设备间是简单的主/从关系,每个器件都可以作为主机也可以作为从机(但同一时刻只能有一个主机),总线上的器件增加和删除不影响其他器件正常工作;I2C总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器。I2C总线可以通过外部连线进行在线检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也有利于标准化和模块化,缩短开发时间。串行的8位双向数据传输速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s。

原理图:

二、通信过程

  1. 主机发送起始信号启用总线
  2. 主机发送一个字节数据指明从机地址和后续字节的传送方向
  3. 被寻址的从机发送应答信号回应主机
  4. 发送器发送一个字节数据
  5. 接收器发送应答信号回应发送器
  6. ........ (循环步骤4、5)
  7.  通信完成后主机发送停止信号释放总线

注意点:

  • 第4步和第5步用的是发送器和接收器,不是主机和从机,这是由第一个字节的最后一位决定主给从发,还是从给主发。也就是说, 第一个字节和最后的停止信号一定是主机发给从机,但中间就不一定了。
  • 发送数据过程中不允许改变发送方向

三、I2C总线的信号类型


开始信号、结束信号和响应信号

开始信号: SCL 为高电平时,SDA由高电平向低电平跳变, 开始传送数据

结束信号:        SCL 为高电平时,SDA由低电平向高电平跳变, 结束传送数据

起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线被释放,处于空闲状态。空闲时,SCL与SDA都是高电平。

停止情况有两种:

  1. 主机不想发了,就发送停止信号;
  2. 从机不想接了,不应答,主机就发送停止信号结束此次通信。

响应信号(ACK)
接收器在接收到8位数据后,在第9个时钟周期,拉低SDA电平

注意:        SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化

四、IIC总线的数据传输格式

        I2C总线通信时每个字节为8位长度,数据传送时,先传送最高位(MSB),后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器,即一帧共有9位。

启动一个传输时,主机先发送S信号,然后发出8位数据。这8位数据中前7位为从机的地址,第8位表示传输的方向(0表示写操作,1表示读操作)。从机收到后会发出一个ACK信号

注意:

      主机接收器在接收到最后一个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,以允许主机发出P信号结束传输。  

五、GPIO模拟I2C时序

代码思路:

gpio_i2c.h

#ifndef __I2C__HEAD__
#define __I2C__HEAD__
#include <stdio.h>
#include "../../include/imx6ull.h"
#include "../gpt/gpt.h"
#include "../uart/uart.h"
extern void gpio_i2c_init();
extern void i2c_generate_start_signal();
extern void i2c_generate_stop_signal();
extern void i2c_master_to_salve_ack();
extern void i2c_master_to_salve_nack();
extern void i2c_write_data(int8_t data);
extern uint_fast8_t i2c_read_data(int8_t data);
extern int wait_salve_to_master_ack_signal();
/*
管脚输入输出电平控制
*/
// 方向控制
#define SCL_OUT_MODE() (GPIO1->GDIR |= (0x1 << 28))
#define SDA_OUT_MODE() (GPIO1->GDIR |= (0x1 << 29))
#define SDA_IN_MODE() (GPIO1->GDIR &= ~(0x1<<29))
#define HIGH 1
#define LOW 0
// 电平状态
#define SDA_INPUT_LEVEL() ((GPIO1->DR & (0x1<<29))?HIGH:LOW )
#define SDA_OUTPUT_LEVEL(level)   do {GPIO1->DR &=~(0x1<<29);GPIO1->DR |= (level << 29);}while (0)
#define SCL_OUTPUT_LEVEL(level)do { GPIO1->DR &=~(0x1 << 28);GPIO1->DR |= (level << 28);}while (0)
#define AP3216_READ_ADDERSS 0x3D
#define AP32_WRITE_AFFRESS 0x3C

#endif // !__

gpio_i2c.c

#include "i2c.h"
// 初始化管脚
void gpio_i2c_init()
{
    // 初始化管脚
    //  UART4_TX_DATA(SCL)
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO28 &= ~(0xf << 0);
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO28 |= (0x5 << 0);
    // UART4_RX_DATA (SDA)
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO29 &= ~(0xf << 0);
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO29 |= (0x5 << 0);

    // 延时50ms
    usgpt_deplay(50);
    return;
}
// 开始信号
void i2c_generate_start_signal()
{
    // 刚开始都是高后来SDC变为低
    // 改为输出模式
    SCL_OUT_MODE();
    SDA_OUT_MODE();
    // 高电平
    SCL_OUTPUT_LEVEL(HIGH);
    SDA_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
    // SDA变为低电平
    SDA_OUTPUT_LEVEL(LOW);
    usgpt_deplay(50);
}
// 结束信号
void i2c_generate_stop_signal()
{
    // 刚开始都为低后来SDC变为高
    SCL_OUT_MODE();
    SDA_OUT_MODE();
    // 低电平
    SCL_OUTPUT_LEVEL(LOW);
    SDA_OUTPUT_LEVEL(LOW);
    usgpt_deplay(50);
    // SDA变为高电平
    SCL_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
    SDA_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
}
// 释放ACK信号
void i2c_master_to_salve_ack()
{
    // 在scl第九个周期时发送信号
    SDA_OUT_MODE();
    SCL_OUTPUT_LEVEL(LOW);
    SDA_OUTPUT_LEVEL(LOW);
    usgpt_deplay(50);
    // scl改变状态
    SCL_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
}
// 释放NACK信号
void i2c_master_to_salve_nack()
{
    // 在scl第九个周期时发送信号
    SDA_OUT_MODE();
    SCL_OUTPUT_LEVEL(LOW);
    SDA_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
    // scl改变状态
    SCL_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
}
// 主机写入信号
void i2c_write_data(int8_t data)
{
    SDA_OUT_MODE();
    SCL_OUTPUT_LEVEL(LOW);
    int8_t i = 0;
    int8_t level = 0;
    for (i = 0; i < 8; i++)
    {
        level = data >> 7 & 1;
        SCL_OUTPUT_LEVEL(level);
        usgpt_deplay(50);
        SCL_OUTPUT_LEVEL(HIGH);
        usgpt_deplay(50);
        SCL_OUTPUT_LEVEL(LOW);
        data = data << 1;
    }
}
// 主机读取信号
uint_fast8_t i2c_read_data(int8_t data)
{
    SDA_OUT_MODE();
    SCL_OUTPUT_LEVEL(LOW);
    usgpt_deplay(50);
    int8_t i = 0;
    int8_t level1 = 0;
    for (i = 0; i < 8; i++)
    {
        level1 = data >> 7 & 1;
        SCL_OUTPUT_LEVEL(level1);
        usgpt_deplay(50);
        data <<= 1;
        data |= SDA_INPUT_LEVEL();
        SCL_OUTPUT_LEVEL(LOW);
        usgpt_deplay(50);
    }
    return data;
}
// 等待从机给主机的应答
//从机会及时响应。如果从机没有响应,代码会生成停止信号并返回错误代码。这可能是I2C通信中错误处理的一部分,用于处理从机没有响应的情况。
int wait_salve_to_master_ack_signal()
{
    // 修改为接收状态
    SDA_IN_MODE();
    SCL_OUTPUT_LEVEL(LOW);
    usgpt_deplay(50);
    SCL_OUTPUT_LEVEL(HIGH);
    usgpt_deplay(50);
    int8_t count = 0;
    int8_t maindate = 0;
    do
    {
        maindate = SDA_INPUT_LEVEL();
        usgpt_deplay(50);
        // 接收到后自然会进行结束
        count++;
        if (count > 50)
        {

            i2c_generate_stop_signal();
            return -1;
        }
    } while (maindate);
    return 0;
}

在SCL底电平状态进行写入,在SCL高电平状态进行写入

六、AP3216C 环境光传感器(ALS)与接近传感器(PS)模块

介绍:
The AP3216C is an integrated ALS & PS module that includes a digital ambient light sensor [ALS], a proximity sensor [PS], and an IR LED in a single package

AP3216C是一个集成式的环境光传感器(ALS)与接近传感器(PS)模块,它将一个数字环境光传感器(ALS)、一个接近传感器(PS)以及一个红外LED集成在一个封装内。

从机地址:

The slave addresses have 7 bits. A read/write bit should be appended to the slave address by the

master device to properly communicate with the device. The slave address of this device is 0x1E.

        从机地址有7位。主设备需要在从机地址后附加一个读写位,以便与该设备正确通信。该设备的从机地址是0x1E

操作时序分析 :

写入协议:

 读出协议:

重点寄存器分析

        在I²C协议中,ALS(环境光传感器)PS(接近传感器)是两种常见的传感器功能。当这两个功能同时激活时,设备将交替运行ALS和PS+IR(红外线传感器)功能。这种模式下,设备的转换时间会增加,通常是单一功能模式下的两倍。以下是详细的运行模式和时间分析:

  1. 交替运行:设备在ALS和PS+IR功能之间交替工作。这意味着设备首先执行ALS的测量,然后切换到PS+IR进行测量,如此循环。

  2. 转换时间增加:由于设备需要在两种模式之间切换,因此完成一次完整的测量周期(包括ALS和PS+IR的测量)所需的时间是单一功能模式下的两倍。这意味着如果单一功能模式下的转换时间是T,那么在同时激活ALS和PS+IR功能时,转换时间将变为2T。

  3. PS等待时间:在这种模式下,PS等待时间被设置为0,意味着在ALS和PS+IR之间的切换是即时的,没有额外的等待时间。

  4. 运行时间:运行时间包括了ALS的测量时间、PS+IR的测量时间以及可能的任何额外处理时间。由于转换时间是原来的两倍,因此总的运行时间也会相应增加。

  5. 数据读取:在这种模式下,主设备需要在两个不同的时间点读取数据,一次是从ALS获取环境光强度数据,另一次是从PS+IR获取接近距离和红外光强度数据。

  6. 应用场景:这种模式适用于需要同时监测环境光强度和接近距离的应用,例如智能手机的自动亮度调节和接近传感器功能。

  7. 注意事项:在设计系统时,需要考虑到转换时间的增加,确保系统有足够的时间来处理两次测量的数据,并且不会因为转换时间的增加而影响整体性能

1、System Configuration Register

The SYSTEM CONFIGURATION register is used to power up/down the device and to select the

ALS and/or PSfeature of the device.

SYSTEM CONFIGURATION”寄存器用于开启/关闭设备电源,并选择设备的ALS(环境光传感器)和/或PS(接近传感器)功能。

对于ALS和PS+IR功能同时激活(011)的情况:

设备将交替运行ALS和PS+IR功能。在此模式下,转换时间将是原来的两倍。运行时间如下所示(PS等待时间=0):

2、PS Configuration

        The PS Configuration register is used to set PS integration time, PS gain, LED waiting time and PS interrupt filter. The PS integration time sets ADC’s sample/conversion time and it will affect both resolution and sensitivity. Longer integration time increases IR ADC&PS ADC resolution and sensitivity.

        PS配置寄存器用于设置PS积分时间、PS增益、LED等待时间和PS中断滤波器。PS积分时间决定了ADC的采样/转换时间,并会影响分辨率和灵敏度。更长的积分时间可以提高IR ADC和PS ADC的分辨率和灵敏度。

        The PS gain setting can be used to increase/decrease the OBJ’s detection distance. Higher gain can increase detection distance but also increase noise or cross talk signal. The IR ADC will not be affected

        PS的ADC通道数据以10位数据的形式分布在两个寄存器中,即PS数据低位和PS数据高位。这两个寄存器分别提供ADC值的低位和高位字节。

        The PS interrupt filter prevents the interrupt triggered by noises. The interrupt is triggered when the PS objectstatus has changed and keeps the change for M consecutive number of conversion time. The PS interrupt filter bitsdetermine the M

        PS中断滤波器用于防止由噪声触发的中断。当中断由PS对象状态的变化触发,并且该变化在连续M次转换时间内保持不变时,才会产生中断。PS中断滤波器位用于确定M的值。

3、PS Data Register

        The ADC channel data for PS is expressed as 10-bit data spread across two registers, PS DataLow and PS DataHigh. These two will provide the lower and higher bytes of the ADC value respectively.

        PS的ADC通道数据以10位数据的形式分布在两个寄存器中,即PS数据低位和PS数据高位。这两个寄存器分别提供ADC值的低位和高位字节。

        The PS object status (OBJ) bit shows the position of object. When object is away from sensor and the count of PS across the threshold of PS, the OBJ bit will be reset to 0. On other way, the object is near the sensor and OBJ bit set to 1 to indicate object closed.

        PS对象状态(OBJ)位显示对象的位置。当对象远离传感器,且PS值超过PS阈值时,OBJ位将被重置为0。反之,当对象靠近传感器时,OBJ位将被设置为1,以表示对象靠近。

        The IR overflow flag (IR_OF) indicates the PS data valid or not. If this bit is set to 1, it indicates that the data ofPS is invalid in high intensive IR light. Please refer to description of IR data

register

        红外溢出标志(IR_OF)用于指示PS数据是否有效。如果此位被设置为1,则表示在高强度红外光下,PS数据无效。请参考红外数据寄存器的描述。

        The higher byte registers can be read after reading the corresponding lower byte register. When the lower byte register is read, the higher byte is stored in a temporary register, which is read bya subsequent read to the higher byte. The higher byte register will read the correct value even ifadditional integration cycles end between the reading of the lower and higher byte data registers.All channel data registers are read-only

        在读取相应的低位字节寄存器后,可以读取高位字节寄存器。当读取低位字节寄存器时,高位字节会被存储在一个临时寄存器中,随后的高位字节读取操作会从这个临时寄存器中读取数据。即使在读取低位和高位数据寄存器之间结束了额外的积分周期,高位寄存器也能读取到正确的值。所有通道数据寄存器都是只读的。

4、ALS Configuration Register

The ALS Configuration register is used to set ALS gain and ALS interrupt filter (persist).


ALS配置寄存器用于设置ALS增益和ALS中断滤波器(持久性)。


ALS Gain (Ambient light detectable range). There are 4 ranges below for AP3216C. (ALS增益(环境光可检测范围)。对于AP3216C,有以下4个范围:)

A. Range 1 (B5B4=’00’): 0 ~ 20661 Lux. Resolution = 0.35 lux/count. 【范围1(B5B4='00'):

0 ~ 20661勒克斯。分辨率=0.35勒克斯/计数。】

B. Range 2 (B5B4=’01’): 0 ~ 5162 Lux. Resolution = 0.0788 lux/count.

C. Range 3 (B5B4=’10’): 0 ~ 1291 Lux. Resolution = 0.0197 lux/count.

D. Range 4 (B5B4=’11’): 0 ~ 323 Lux. Resolution = 0.0049 lux/count

ALS ADC data to Lux conversion formula as below :

Ambient Light (lux) = 16 bit ALS ADC data * Resol

 ALS ADC数据到勒克斯的转换公式如下:

环境光(勒克斯)= 16位ALS ADC数据×分辨率

ALS Interrupt Filter: Configurable interrupt filtering is to provide hardware interrupt to be generated after interrupts trigger for N consecutive numbers of conversion time. The ALS interrupt filter bits determine N.

        ALS中断滤波器:可配置的中断滤波功能是在连续N次转换时间后产生硬件中断。ALS中断滤波器的位决定了N的值。

有4位设置可供选择,例如:

  1. B3B2B1B0='0000',N = 1
  2. B3B2B1B0='0001',N = 4
  3. B3B2B1B0='0010',N = 8
  4. B3B2B1B0='0011',N = 12
  5. B3B2B1B0='0100',N = 16
  6. B3B2B1B0='1111',N = 60
5 、ALS Data Register

The ADC channel data for ALS is expressed as 16-bit data spread across two registers, ALS DataLow and ALS Data High. These two will provide the lower and higher bytes of the ADC value

respectively. All channel data registers are read-only.

        ALS的ADC通道数据以16位数据的形式分布在两个寄存器中,即ALS数据低位(ALS Data Low)和ALS数据高位(ALS Data High)。这两个寄存器分别提供ADC值的低位和高位字节。所有通道数据寄存器都是只读的。

The higher byte registers can be read after reading the lower byte register. When the lower byte

register is read, the higher byte is stored in a temporary register, which is read by a subsequent

read to the higher byte. The higher byte register will read the correct value even if additional

integration cycles end between the reading of the lower and higher byte data registers.

        在读取低位字节寄存器之后,可以读取高位字节寄存器。当读取低位字节寄存器时,高位字节会被存储在一个临时寄存器中,随后的对高位字节的读取操作会从这个临时寄存器中读取数据。即使在读取低位和高位字节数据寄存器之间有其他积分周期结束,高位字节寄存器也会读取到正确的值。

七、GPIO模拟I2C读取环境光传感器数据

ap3216c_sensor.h:

#ifndef __AP3216__HEAD__
#define __AP3216__HEAD__
#include "../../include/imx6ull.h"
#include "../uart/uart.h"
#include "../gpt/gpt.h"
#include "./i2c.h"
#include <stdint.h>
#include <stdio.h>
#define AP3216_READ_ADDRESS 0x3D
#define AP3216_WRITE_ADDRESS 0x3C
#define AP3216_SYSTEM_CONFIGRATION  0x00
#define AP3216_PS_CONFIGRATION_ADDRESS 0x20
#define AP3216_PS_DATA_LOW_ADDRESS 0x0E
#define AP3216_PS_DATA_HIGH_ADDRESS 0x0F
#define AP3216_ALS_CONFIGRATION_ADDRESS 0x10
#define AP3216_ALS_DATA_LOW_ADDRESS 0x0C
#define AP3216_ALS_DATA_HIGH_ADDRESS 0x0D

#endif // !__AP3216__HEAD__

ap3216c_sensor.c:

  • 根据时序图进行代码编写
  • 每发送一次就进行等待从机应答
  • 连续读或写都要进行延时
#include "ap3216c_sensor.h"
// 按照时序图把相应功能完成
// 写入数据
void ap3216_write_data_to_sensor(uint_fast8_t registerAddr, uint_fast8_t val)
{
    // 1、发送信号
    i2c_generate_start_signal();
    // 2、发送从机地址加写位
    i2c_write_data(AP3216_WRITE_ADDRESS);
    // 等待从机应答
    int ret;
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return; /* code */
    }
    // 4、发送寄存器地址
    i2c_write_data(registerAddr);
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return;
    }
    // 5、向对应寄存器写入数据
    i2c_write_data(val);
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return;
    }
    // 6、发送完成后给一个停止信号
    i2c_generate_stop_signal();
}
// 读取数据
uint_fast8_t ap3216_read_data_to_sensor(uint_fast8_t registerAddr)
{
    uint8_t data;
    int ret;
    // 1、发送信号
    i2c_generate_start_signal();
    // 2、发送从机地址加写位
    i2c_write_data(AP3216_WRITE_ADDRESS);
    // 3、等待从机应答
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return; /* code */
    }
    // 4、发送寄存器地址
    i2c_write_data(registerAddr);
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return;
    }
    // 5、重新发送开始信号(SR)
    i2c_generate_start_signal();
    // 6、发送从机地址加R位
    i2c_write_data(AP3216_READ_ADDRESS);
    ret = wait_salve_to_master_ack_signal();
    if (ret == -1)
    {
        return;
    }
    // 7、读取寄存器的值
    data = i2c_read_data(registerAddr);
    // 8、主机发送nack信号
    i2c_master_to_salve_nack();
    // 9、发送停止信号
    i2c_generate_stop_signal();
    return data;
}
// 配置ap3216c连续两次读或写都要进行延时
void ap3216c_sensor_config()
{
    // 进行复位
    ap3216_write_data_to_sensor(AP3216_PS_CONFIGRATION_ADDRESS, 4);
    msgpt_deplay(10);
    // 激活als
    ap3216_write_data_to_sensor(AP3216_ALS_CONFIGRATION_ADDRESS, 3);
    msgpt_deplay(10);
    // 读取系统配置的值
    uint_fast8_t ret = (AP3216_ALS_CONFIGRATION_ADDRESS);
    if (ret == 0x3)
    {
        uart_printf("ap3216 success conminution");
    }
}
// 读取als数据
uint_fast16_t ap3216_sener_read_als()
{
    // 初始化管脚
    gpio_i2c_init();
    // 配置ap3216传感器
    ap3216c_sensor_config();
    // 读取数据
    uint_least16_t data = 0;
    uint8_t datalow = ap3216_read_data_to_sensor(AP3216_ALS_DATA_LOW_ADDRESS);
    uint8_t datahigh = ap3216_read_data_to_sensor(AP3216_ALS_DATA_HIGH_ADDRESS);
    data = datahigh << 8 | datalow;
    return data;
}
// 读取ps数据
uint_fast16_t ap3216_sener_read_ps()
{
    // 初始化管脚
    gpio_i2c_init();
    // 配置ap3216传感器
    ap3216c_sensor_config();
    // 读取数据
    uint_fast16_t data = 0;
    uint_fast8_t datalow = ap3216_read_data_to_sensor(AP3216_PS_DATA_LOW_ADDRESS);
    uint_fast8_t datahigh = ap3216_read_data_to_sensor(AP3216_PS_DATA_HIGH_ADDRESS);
    if (datahigh & 0x40)
    {
        uart_printf("Invalid IR\r\n");
        return 0;
    }
    /*从AP3216传感器读取红外(IR)数据,存储在datahigh变量中。
    检查datahigh的第6位是否为1,如果是,表示数据无效,打印错误信息并返回0。
    如果数据有效,执行以下操作:
        保留datahigh的低5位。
        将结果左移4位。
        将datalow的低4位合并到结果的低4位。
        返回最终处理后的数据。*/
    else
    {
        data = datahigh & (0x3f);
        data <<= 4;
        data |= ((datalow) & 0xf);
        return data;
    }
}
void ap3216_sernor_als_test()
{
    while (1)
    {
        uint_fast8_t ret = ap3216_sener_read_als();
        uart_printf("als:%d\r\n", ret);
        sgpt_deplay(1);
    }
}
void ap3216_sernor_ps_test()
{
    while (1)
    {
        uint_fast8_t ret1 = ap3216_sener_read_ps();
        uart_printf("als:%d\r\n", ret1);
        sgpt_deplay(1);
    }
}

八、i.MX6ull I2C控制器介绍

The Inter IC (I2C) provides functionality of a standard I2C slave and master. The I2C is designed

to be compatible with the standard NXP I2C bus protocol.

Inter IC (I2C)提供标准I2C从机和主机的功能。I2C被设计为兼容标准NXP I2C总线协议。

I2C is a two-wire, bidirectional serial bus that provides a simple, efficient method of data

exchange, minimizing the interconnection between devices. This bus is suitable for applications

requiring occasional communications over a short distance between many devices. The flexible

I2C standard allows additional devices to be connected to the bus for expansion and system

development.

I2C是一种双线双向串行总线,它提供了一种简单有效的数据交换方法,

最大限度地减少了设备之间的互连。这种总线适用于需要在许多设备之间

偶尔进行短距离通信的应用。灵活的I2C标准允许将其他设备连接到总线

上,以进行扩展和系统开发。

Clocks
There are two input clocks for I2C.

The following table describes the clock sources for I2C. Please see Clock Controller Module (CCM)for clock setting, configuration and gating information

I2C有两个输入时钟

I2C的时钟源说明如下表所示。请参阅时钟控制器模块(CCM)时钟设置,配置和门控信息。

Peripheral clock: This clock is used for peripheral bus register read/writes.
外围时钟:这个时钟用于外围总线寄存器读/写。
Module clock: This is the functional clock of the I2C. The serial bit clock frequency is derived from

the module clock. The module clock and peripheral clocks are synchronous with each other. The

minimum frequency of the module clock should be 12.8 MHz for Fast mode to achieve 400-kbps

operation.

模块时钟:这是I2C的功能时钟。串行位时钟频率来源于模块时钟。模块时钟与外设时钟是同步的。在Fast模式下,模块时钟的最小频率应为12.8 MHz,以实现400kbps的工作。

Arbitration procedure(仲裁程序)
If multiple devices simultaneously request the bus, the bus clock is determined by a

synchronization procedure in which the low period equals the longest clock-low period among the

devices, and the high period equals the shortest. A data arbitration procedure determines the

relative priority of competing devices.

如果多个设备同时请求总线,总线时钟由一个同步过程确定,其中低周期等于设备中最长的时钟低周期,高周期等于最短的时钟低周期。数据仲裁程序决定了竞争设备的相对优先级。

A device loses arbitration if it sends logic high while another sends logic low; it immediately

switches to Slave Receive mode and stops driving I2Cn_SDA. In this case,the transition from

master to Slave mode does not generate a Stop condition.Meanwhile,hardware sets the arbitration lost bit in the I2C Status register (I2C_I2SR[IAL] to indicate loss of arbitration).

如果一个设备发送高逻辑而另一个发送低逻辑,则失去仲裁;立即切换到 Slave Receive模式,停止驱动I2Cn_SDA。在这种情况下,从主模式到从模式的转换不会生成Stop条件。同时,硬件在I2C状态寄存器(I2C_I2SR[IAL]表示仲裁丢失)中设置仲裁丢失位。

Initialization sequence(初始化顺序)
Before the interface can transfer serial data, registers must be initialized, as listed here.

1. Set the data sampling rate (I2C_IFDR[IC] to obtain SCL frequency from the system bus clock.

2. Update the address in the (I2C_IADR) to define its slave address (address can range from 0

to 0x7f).

3. Set the I2C enable bit (I2C_I2CR[IEN]) to enable the I2C bus interface system.

4. Modify the bits in the I2C_I2CR to select Master/Slave mode, Transmit/Receive mode, and

Interrupt-Enable or not.

在接口能够传输串行数据之前,必须初始化寄存器,如下所列。

1. 设置数据采样率(I2C_IFDR[IC]),从系统总线时钟获取SCL频率。

2. 更新(I2C_IADR)中的地址以定义其从地址(地址范围从0到0x7f)。

3. 设置I2C使能位(I2C_I2CR[IEN]),使能I2C总线接口系统。

4. 修改I2C_I2CR中的位,使其选择“主从模式”、“收发模式”和“中断使能”。

Generation of Start(Start的生成)


After completion of the initialization procedure, serial data can be transmitted by selecting the

Master Transmit mode. On a multimaster bus system, the busy bus (I2C_I2SR[IBB]) must be tested to determine whether the serial bus is free. If the bus is free (IBB = 0), the Start signal and the first byte (the slave address) can be sent. The data written to the data register comprises the

address of the desired slave and the LSB indicates the transfer direction.

初始化程序完成后,可以通过选择主传输模式来发送串行数据。在多主总线系统中,需要测试忙碌的总线(I2C_I2SR[IBB])以确定串行总线是否空闲。如果总线是空闲的(IBB = 0),则可以发送起始信号和第一个字节(从机地址)。写入数据寄存器的数据包括所需从机的地址,并且最低有效位指示传输方向。

The free time between a Stop and the next Start condition is built into the hardware that

generates the Start cycle. Depending on the relative frequencies of the system clock and the SCL

period, it may be necessary to wait until the I2C is not busy after writing the calling address to

the data register (I2C_I2DR), before proceeding to load data into the data register (I2C_I2DR).

停止和下一个开始条件之间的空闲时间内置在生成开始周期的硬件中。根据系统时钟和SCL周期的相对频率,在将调用地址写入数据寄存器(I2C_I2DR)后,可能需要等到I2C不忙,然后再将数据加载到数据寄存器(I2C_I2DR)中

Generation of Stop(停止的产生)
A data transfer ends when the master signals a Stop, which can occur after all data is sent. For a

master receiver to terminate a data transfer, it must inform the slave transmitter by not

acknowledging the last data byte. This is done by setting the transmit acknowledge bit

(I2C_I2CR[TXAK]) before reading the next-to-last byte. Before the last byte is read, a Stop signal

must be generated.

数据传输结束时,主站发出停止信号,这可能发生在所有数据发送后。对于主接收器终止数据传输,它必须通过不确认最后一个数据字节来通知从站发送器。这是通过在读取倒数第二个字节之前设置传输确认位(I2C_I2CR[TXAK])来完成的。在读取最后一个字节之前,必须生成停止信号。

Generation of Repeated Start(重复启动的产生)
After the data transfer, if the master still requires the bus, it can signal another Start followed by

another slave address without signaling a Stop.

在数据传输之后,如果主站仍然需要总线,它可以发出另一个启动信号,后面跟着另一个从站地址,而不用发出停止信号。

Post-transfer software response(传输后软件响应)

Sending or receiving a byte sets the data transferring bit (I2C_I2SR[ICF]), which indicates one

byte of communication is finished. Upon completion, the interrupt status (I2C_I2SR[IIF]) is also set.An external interrupt is generated if the interrupt enable (I2C_I2CR[IIEN]) is set. The software

must first clear the interrupt status (I2C_I2SR[IIF]) in the interrupt routine.

发送或接收一个字节设置数据传输位(I2C_I2SR[ICF]),表示一个字节的通信完成。完成后,中断状态(I2C_I2SR[IIF])也被设置。如果设置了中断使能(I2C_I2CR[IIEN]),则会产生外部中断。软件必须首先清除中断例程中的中断状态(I2C_I2SR[IIF])。

The data transferring bit (I2C_I2SR[ICF]) is cleared either by reading from I2C_I2DR in Receive

mode or by writing to this register in Transmit mode.

数据传输位(I2C_I2SR[ICF])通过在接收模式下从I2C_I2DR读取或在传输模式下写入该寄存器来清除。

九、I2C控制器重点寄存器介绍

1、I2C Address Register (I2Cx_IADR)

The I2C_IADR holds the address to which the I2C responds when addressed as a slave. The slave address is not the address sent on the bus during the address transfer. The register is not reset by a software reset.

I2C_IADR保存I2C作为从属地址时响应的地址。从地址不是在地址传输期间在总线上发送的地址。寄存器不能通过软件复位来复位。

2、I2C Frequency Divider Register (I2Cx_IFDR)


The I2C_IFDR provides a programmable prescaler to configure the clock for bit-rate selection.

The register does not get reset by a software reset.

I2C_IFDR提供了一个可编程的预分频器来配置比特率选择的时钟。寄存器不会通过软件复位的方式进行复位。

I2C clock is sourced from PERCLK_ROOT which is routed from IPG_CLK_ROOT. I2C clock

frequency can easily obtained by using the following formula: I2C clock Frequency =

(PERCLK_ROOT frequency)/(division factor corresponding to IFDR)

I2C时钟的来源是PERCLK_ROOT,其由IPG_CLK_ROOT路由提供。I2C时钟频率可以很容易地通过下面的公式得到:I2C时钟频率= (PERCLK_ROOT频率)/(IFDR对应的除法因子)

3、I2C Control Register (I2Cx_I2CR)
The I2C_I2CR is used to enable the I2C and the I2C interrupt. It also contains bits that govern

operation as a slave or a master.

I2C_I2CR被用来启用I2C和I2C中断。它还包含控制操作为从设备还是主设备的位。

该块已禁用,但寄存器仍然可以被访问。

I2C已启用。在其他I2C_I2CR位生效之前,必须设置此位。

0 I2C中断被禁用,但是当发生中断条件时,状态标志I2C_I2SR[IIF]仍然被设置。

1 I2C中断已启用。如果I2C_I2SR[IIF]被设置,将发生I2C中断。

Master/Slave mode select bit. If the master loses arbitration, MSTA is cleared without generating a Stop signal.

NOTE:

The module clock should be on for writing to the MSTA bit.

The MSTA bit is cleared by software to generate a Stop condition; it can also be cleared by

hardware when the I2C loses the bus arbitration.

主从模式选择位。如果主仲裁失败,MSTA将被清除,而不会产生停止信号。

注意:

模块时钟应该开启,以便写入MSTA位。

MSTA位由软件清除以产生停止条件;当I2C失去总线仲裁时,也可以由硬件清除。

0 Slave mode. Changing MSTA from 1 to 0 generates a Stop and selects Slave mode.

1 Master mode. Changing MSTA from 0 to 1 signals a Start on the bus and selects Master mode.

0 从机模式。将MSTA从1修改为0会产生Stop,并选择Slave模式。

1 主机模式。将MSTA从0更改为1表示在总线上启动并选择主模式。

Transmit/Receive mode select bit. Selects the direction of master and slave transfers.
发送/接收模式选择位。选择主从传输的方向。
 0 Receive.

When a slave is addressed, the software should set MTX according to the slave read/write bit in

the I2C status register (I2C_I2SR[SRW])

当对从机进行寻址时,软件应该根据I2C状态寄存器(I2C_I2SR[SRW])中的从机读/写位设置MTX。

1 Transmit.

In Master mode, MTX should be set according to the type of transfer required. Therefore, for

address cycles, MTX is always 1.

在主模式下,MTX应根据所需的传输类型进行设置。因此,对于地址周期,MTX总是1。

Writing TXAK applies only when the I2C bus is a receiver.
写入TXAK仅适用于I2C总线为接收器时。
0 An acknowledge signal is sent to the bus at the ninth clock bit after receiving one byte of data.
接收到一个字节的数据后,在第9个时钟位向总线发送确认信号。
1 No acknowledge signal response is sent (that is, the acknowledge bit = 1).
1 未发送确认信号响应(即确认位= 1)。

0 No repeat start

1 Generates a Repeated Start condition

0 不能重复开始

1 生成重复开始条件

4、I2C Status Register (I2Cx_I2SR)

The I2C_I2SR contains bits that indicate transaction direction and status.
I2C_I2SR寄存器包含了指示传输方向和状态的位。

0 An "acknowledge" signal was received after the completion

of an 8-bit data transmission on the bus.

0 在总线上进行8位数据传输完成后,收到了一个“确认”信号

1 A "No acknowledge" signal was detected at the ninth clock.

1 在第9钟时检测到“不确认”信号。

5、I2C Data I/O Register (I2Cx_I2DR)

In Master Receive mode, reading the data register allows a read to occur and initiates the next

byte to be received. In Slave mode, the same function is available after it is addressed.

在主接收模式下,读取数据寄存器允许读取并启动下一个要接收的字节。在Slave模式下,寻址后可以使用相同的功能。

十、i.MX6ull I2C控制器操作

 代码思路:

 

i2c_contraller.h 

#ifndef __I2C__CONTROLLER_HEAD__
#define __I2C__CONTROLLER_HEAD__
#include "../../include/imx6ull.h"
#include "../gpt/gpt.h"
#include "../uart/uart.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
extern void i2c_controller_gpio_init();
extern void i2c_controller_init(I2C_Type *i2c);
extern void i2c_controller_generate_strat_signal(I2C_Type *i2c);
extern void i2c_controller_generate_repeat_start_signal(I2C_Type *i2c);
extern void i2c_controller_generate_stop_signal(I2C_Type *i2c);
extern void i2c_controller_send_slave_adderss_rw(I2C_Type *i2c, uint_fast8_t slaveadderss_rw);
extern void i2c_controller_send_data(I2C_Type *i2c, uint_fast8_t *buf, uint_fast16_t size);
extern void i2c_controller_read_data(I2C_Type *i2c, uint_fast8_t *buf, uint_fast16_t size);
#endif // !__

 i2c_contraller.c

#include "i2c_contraller.h"
// 管脚初始化
void i2c_controller_gpio_init()
{
    // 修改频率
    // 使能
    // 主从机输出方向
    // 更新从机地址
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO28 &= ~(0xf << 0);
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO28 |= (0x2 << 0);
    // UART4_RX_DATA (SDA)
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO29 &= ~(0xf << 0);
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO29 |= (0x2 << 0);
    CCM->CCGR2 |= (0x3 << 6);
    return;
}
// 控制器初始化
void i2c_controller_init(I2C_Type *i2c)
{

    // disable
    i2c->I2CR &= ~(0x1 << 7);
    // 设置分频
    i2c->IFDR = 0x0f;
    // able
    i2c->I2CR |= (0x1 << 7);
}
// 产生开始信号
void i2c_controller_generate_strat_signal(I2C_Type *i2c)
{
    // 清理中断标识:
    i2c->I2SR &= ~(0x1 << 1);
    // 更换模式变为主机模式
    i2c->I2CR &= ~(0x1 << 5);
    i2c->I2CR |= (0x1 << 5);
}
// 产生重复开始信号
void i2c_controller_generate_repeat_start_signal(I2C_Type *i2c)
{
    // 清理中断
    i2c->I2SR &= ~(0x1 << 1);
    // 重新发送信号
    i2c->I2CR |= (0x1 << 2);
}
// 产生停止信号
void i2c_controller_generate_stop_signal(I2C_Type *i2c)
{
    msgpt_deplay(100);
    i2c->I2SR &= ~(0x1 << 1);

    i2c->I2CR |= (0x1 << 5);
    i2c->I2CR &= (0x1 << 5);
}
// 发送从机地址和读写位
void i2c_controller_send_slave_adderss_rw(I2C_Type *i2c, uint_fast8_t slaveadderss_rw)
{
    // 设置主机发送模式
    i2c->I2CR |= (0x1 << 4);
    // 发送数据
    i2c->I2DR = slaveadderss_rw;
    // 等待数据发送完成
    int flag;
    do
    {
        flag = i2c->I2SR & (0x1 << 7);
    } while (!flag);
#if 0
    /*
    有第七位和第一位的选择写法都一样
    */
#endif
    // 清理中断
    i2c->I2SR &= ~(0x1 << 1);
}
// 写入数据
void i2c_controller_send_data(I2C_Type *i2c, uint_fast8_t *buf, uint_fast16_t size)
{
    // 设置主机为发送模式
    i2c->I2CR |= (0x1 << 4);
    // 循环发送数据
    int i;
    for (i = 0; i < size; i++)
    {
        i2c->I2DR = *(buf + i);
        // 等待发送完成
        int flag;
        do
        {
            flag = i2c->I2SR & (0x1 << 7);
        } while (!flag);
        // 发送完成后,清理中断标识
        i2c->I2SR &= ~(0x1 << 1);
    }
}
// 读取数据
void i2c_controller_read_data(I2C_Type *i2c, uint_fast8_t *buf, uint_fast16_t size)
{
    // 设计主机为接受状态
    i2c->I2CR &= ~(0x1 << 4);
    // 设置ACK信号循环接收数据
    int invalidVal = (i2c->I2DR);
    int i;
    for (i = 0; i < size; i++)
    {
        if (i == size - 1)
        {
            i2c->I2CR |= (0x1 << 3);
        }

        // 等待接收完成
        int flag;
        do
        {
            flag = i2c->I2SR & (0x1 << 1);
        } while (!flag);
        // 接收数据
        *(buf + i) = (i2c->I2DR & 0xff);
        // 清理中断标识
        i2c->I2SR &= ~(0x1 << 1);
    }

}

ap3216c_contraller_sensor.h 

#ifndef __AP3216_CONTRALLER_SENSOR_HEAD__
#define __AP3216_CONTRALLER_SENSOR_HEAD__
#include "../../include/imx6ull.h"
#include "../uart/uart.h"
#include "../gpt/gpt.h"
#include "./i2c_contraller.h"
#include <stdint.h>
#include <stdio.h>
#define AP3216_READ_ADDRESS 0x3D
#define AP3216_WRITE_ADDRESS 0x3C
#define AP3216_SYSTEM_CONFIGRATION  0x00
#define AP3216_PS_CONFIGRATION_ADDRESS 0x20
#define AP3216_PS_DATA_LOW_ADDRESS 0x0E
#define AP3216_PS_DATA_HIGH_ADDRESS 0x0F
#define AP3216_ALS_CONFIGRATION_ADDRESS 0x10
#define AP3216_ALS_DATA_LOW_ADDRESS 0x0C
#define AP3216_ALS_DATA_HIGH_ADDRESS 0x0D


extern void ap3216_controller_sensor_config();
extern uint_fast8_t i2c_controller_read_data_from_sensor(uint_fast8_t registerAddr);
extern void i2c_controller_write_date_to_sensor(uint_fast8_t registerAddr,uint_fast8_t val);
extern void i2c_controller_and_pins_init();
extern uint_fast16_t ap3216_control_sensor_read_data_als();
#endif // !__AP3216__HEAD__
 

 ap3216c_contraller_sensor.c

#include "ap3216c_contraller_sensor.h"

// 管脚初始化
void i2c_controller_and_pins_init()
{
    i2c_controller_gpio_init();
    i2c_controller_init(I2C1);
}
// 发送信号
void i2c_controller_write_date_to_sensor(uint_fast8_t registerAddr, uint_fast8_t val)
{
    // 1、发送信号
    i2c_controller_generate_strat_signal(I2C1);
    // 2、发送从机地址加写位
    i2c_controller_send_slave_adderss_rw(I2C1, AP3216_WRITE_ADDRESS);
    // 4、发送寄存器地址
    uint_fast8_t buf[] = {registerAddr, val};
    i2c_controller_send_data(I2C1, buf, sizeof(buf));
    // 发送信号
    i2c_controller_generate_stop_signal(I2C1);
}
uint_fast8_t i2c_controller_read_data_from_sensor(uint_fast8_t registerAddr)
{
    uint8_t data;
    int ret;
    // 1、发送信号
    i2c_controller_generate_strat_signal(I2C1);
    // 2、发送从机地址加写位
    i2c_controller_send_slave_adderss_rw(I2C1, AP3216_WRITE_ADDRESS);

    // 4、发送寄存器地址
    uint_fast8_t buf[] = {registerAddr};
    i2c_controller_send_data(I2C1, buf, sizeof(buf));
    // 5、重新发送开始信号(SR)
    i2c_controller_generate_repeat_start_signal(I2C1);
    // 6、发送从机地址加R位
    i2c_controller_send_slave_adderss_rw(I2C1, AP3216_READ_ADDRESS);

    // 7、读取寄存器的值
    uint_fast8_t rbuf[1] = {0};
    i2c_controller_read_data(I2C1, rbuf, sizeof(rbuf));
    // 8、主机发送nack信号
    i2c_controller_generate_stop_signal(I2C1);
    // 9、发送停止信号
    return *rbuf;
}
// 配置ap3216传感器
void ap3216_controller_sensor_config()
{

    i2c_controller_write_date_to_sensor(AP3216_SYSTEM_CONFIGRATION, 4);
    msgpt_deplay(5);
    i2c_controller_write_date_to_sensor(AP3216_SYSTEM_CONFIGRATION, 3);
    msgpt_deplay(5);
    uint_fast8_t ret = i2c_controller_read_data_from_sensor(AP3216_SYSTEM_CONFIGRATION);
    if (ret == 0x3)
    {
        uart_printf("ap3216 communication success\r\n");
    }
}

// 读光环境传感器(ALS)数据
uint_fast16_t ap3216_control_sensor_read_data_als()
{
    uint_fast16_t data = 0;

    uint_fast16_t data_low = i2c_controller_read_data_from_sensor(AP3216_ALS_DATA_LOW_ADDRESS);
    msgpt_deplay(50);
    uint_fast16_t data_high = i2c_controller_read_data_from_sensor(AP3216_ALS_DATA_HIGH_ADDRESS);
    data = data_high << 8 | data_low;
    return data;
}

void test_ap3216_control_sensor_als()
{
     
    i2c_controller_gpio_init();
    
    ap3216_controller_sensor_config();

    while (1)

    {
        uint_fast16_t result = ap3216_control_sensor_read_data_als();
        uart_printf("ALS=%d\r\n", result);
        sgpt_deplay(1);
    }
}

// 读取PS数据
uint_fast16_t ap3216_control_sensor_read_data_ps()
{
    uint_fast16_t data = 0;
    uint_fast16_t data_low = i2c_controller_read_data_from_sensor(AP3216_PS_DATA_LOW_ADDRESS);
    msgpt_deplay(50);
    uint_fast16_t data_high = i2c_controller_read_data_from_sensor(AP3216_PS_DATA_HIGH_ADDRESS);
    if (data_high & 0x40)

    {
        uart_printf("Invalid IR\r\n");
        return 0;
    }
    else
    {
        data = data_high & (0x3f);
        data <<= 4;
        data |= ((data_low) & 0xf);
        return data;
    }
}
void test_ap3216_control_sensor_ps()
{
        i2c_controller_and_pins_init();
        ap3216_controller_sensor_config();
    while (1)

    {
        uint_fast16_t result = ap3216_control_sensor_read_data_ps();
        uart_printf("PS=%d\r\n", result);
        sgpt_deplay(1);
    }
}

  • 这是本人的学习笔记不是获利的工具,小作者会一直写下去,希望大家能多多监督
  • 文章会每攒够两篇进行更新发布(受平台原因,也是希望能让更多的人看见)
  • 感谢各位的阅读希望我的文章会对诸君有所帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值