当前位置: 首页 > article >正文

I2C模块理解

I2C模块理解

文章目录

    • I2C模块理解
      • 1.配置I2C
      • 2.信号
      • 3.数据传输
        • 3.1主机发送
        • 3.2主机接收
        • 3.3从机发送
        • 3.4从机接收
      • 4.中断传输
      • 5.Aardvark

1.配置I2C

I2C的特征

  1. 只需要两条公共总线(线)即可控制I2C网络上的任何设备
  2. 无需像UART通信那样事先约定数据传输速率。因此可以根据需要随时调整数据传输速度
  3. 用于验证传输数据的简单机制
  4. 使用7位寻址系统来定位I2C总线上的特定设备
  5. I2C网络易于扩展,新设备可以简单地连接到两条公共I2C总线上
image-20230308132412626

image-20230314101137773

I2C优缺点

1.由于只需要两根电线,I2C 非常适合有许多设备连接在总线上的电路板。这有助于降低成本和电路的复杂性作为额外的设备添加到系统。

2.由于只有两条连接,因此处理寻址和确认的开销会更加复杂。这在简单的配置中可能效率低下,可能首选 SPI 这样的直接链接接口。

2.信号

1.起始信号

I2C协议规定,SCL处于高电平时,SDA由高到低变化,这种信号是起始信号。

2.停止信号

I2C协议规定,SCL处于高电平,SDA由低到高变化,这种信号是停止信号。

image-20230313195217681

3.数据有效性

I2C协议对数据的采样发生在SCL高电平期间,除了起始和停止信号,在数据传输期间,SCL为高电平时,SDA必须保持稳定,不允许改变,在SCL低电平时才可以进行变化。

image-20230313194953080

4.应答信号与非应答信号

应答信号出现在1个字节传输完成之后,即第9个SCL时钟周期内,此时主机需要释放SDA总线,把总线控制权交给从机

如果总线之前为高电平,从机控制总线从高电平到低电平,则为应答信号

如果总线之前为高电平,从机控制总线持续高电平,则为非应答信号

3.数据传输

传输模式SDASCL备注
主机发送模式输出输出主机发送一系列数据到从机。一个开始条件(S),随后一个从机地址,(SLA)+写控制字(W),表示进入主机发送模式。
主机接收模式输入输出主机接收模式中,主机从从机接收一系列数据。一个开始条件(S),随后一个从机地址,(SLA)+读控制字®表示进入主机接收模式。
从机接收模式输入输入从机接收模式中,从机从主机接收一系列数据,进入从机模式前,需设置从机地址
从机发送模式输出输入从机发送模式中,从机发送一系列数据到主机。进入从机模式前,需设置从机地址

I2C 数据传输是以8-bit 进行双向数据传输,标准模式下可达100kbit/s 的传输速率,快速模式可达400kbit/s 的传输速率。

I2C 协议的一个示例。

image-20230314101742837

3.1主机发送

示意图:

image-20230307193159819
void HT_I2C_Master_Transmit(HT_I2C_TypeDef *I2Cx, uint8_t DevAddress, uint8_t *pData, uint16_t Size)
{
    // 1.查询I2C 控制寄存器中的开始标志位, 如果空闲则生成开始信号, 否则不会生成开始信号
    HT_I2C_start(I2Cx);                         
    // 2.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
    while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}  

    if(I2Cx == HT_I2C0)
    {
        // 3.查询I2C 状态寄存器中是否未启动标志  08H -> 起始条件已被发送
        if(HT_I2C0->I2CSTA == I2C_START)
        {
            // 4.写入I2C 数据寄存器 从机地址
            HT_I2C0->I2CDAT = I2C_ADD_WRITE (DevAddress);
            // 5.清空I2C 控制寄存器中的中断标志位
            HT_I2C0->I2CCON &= ~I2C_I2CCON_SI; /* Clear the SI bit*/        
        }
        else return;
        // 6.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
        while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}      
        // 7.查询I2C 状态寄存器是否回复ACK 0X18H为 SLA+W 已被发送 ACK 已被接收
        if(HT_I2C0->I2CSTA!= I2C_MASTER_TRANS_ADDR_ACK)     
        {
            return;
        }
        else
        {
            while (Size > 0)  /* send data to slave */
            {
                // 8.写入I2C 数据寄存器的值 遍历pData数组
                HT_I2C0->I2CDAT = *(pData++);  
                // 9.清空I2C 控制寄存器中的中断标志位                                 
                HT_I2C0->I2CCON &= ~I2C_I2CCON_SI; /* Clear the SI bit*/        
                // 10.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
                while (HT_I2C_ITFlagStatusGet (I2Cx)== RESET) {;}               
                // 11.查询I2C 状态寄存器是否回复ACK 0X28H为 数据字节已被发送, ACK 已被接收
                if (HT_I2C0->I2CSTA != I2C_MASTER_TRANS_DATA_ACK)               
                {
                    return;
                }
            }
    }
    // 12.清空I2C 控制寄存器中的停止标志位 1:当处于主机模式,则向总线传输停止信号 0:不向总线传输停止信号 当前为主机模式
    HT_I2C0->I2CCON |= I2C_I2CCON_STO;
    // 13.清空I2C 控制寄存器中的中断标志位
    HT_I2C0->I2CCON&= ~I2C_I2CCON_SI;
}

3.2主机接收

image-20230308132802252

void HT_I2C_Master_Receive (HT_I2C_TypeDef *I2Cx,uint8_t DevAddress, uint8_t *pData, uint16_t Size)
{
    // 1.查询I2C 控制寄存器中的开始标志位, 如果空闲则生成开始信号, 否则不会生成开始信号
    HT_I2C_Start (I2Cx);        /*Generate start condition*/   
    // 2.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
    while (HT_I2C_ITFlagStatusGet (I2Cx)== RESET) {;}          

    if (I2Cx == HT_I2C0)
    {
        // 3.查询I2C 状态寄存器中是否未启动标志  08H -> 起始条件已被发送
        if (HT_I2C0->I2CSTA== I2C_START) /*start condition transmitted*/           
        {
            // 4.读取I2C 数据寄存器 从机地址
            HT_I2C0->I2CDAT = I2C_ADD_READ (DevAddress); /* Send Slave address */   
            // 5.清空I2C 控制寄存器中的中断标志位
            HT_I2C0->I2CCON &= ~I2C_I2CCON_SI; /* Clear the SI bit*/                
        }
        else return;
        // 6.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
        while (HT_I2C_ITFlagStatusGet (I2Cx)== RESET) {;}   
        // 7.查询I2C 状态寄存器是否  40H SLA+R 已被发送; ACK 已被接收        
        if (HT_I2C0->I2CSTA!= I2C_MASTER_RCV_ADDR_ACK)              

        {
            return;
        }
        else
        {
            while (Size >0)
            {
                // 8.将获取到的数据放入数组pData中
                *(pData++) = HT_I2C0->I2CDAT ;          /* 获取数据*/             
                // 9.清空I2C 控制寄存器中的中断标志位
                HT_I2C0->I2CCON &= ~I2C_I2CCON_SI;      /* Clear the SI bit*/   
                // 10.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
                while (HT_I2C_ITFlagStatusGet (I2Cx)== RESET) {;}               
                // 11. 50H 数据字节已被接收;已返回ACK
                if (HT_I2C0->I2CSTA != I2C_MASTER_RCV_DATA_ACK)                 
                {
                    return;
                }
            }

        }
        HT_I2C0->I2CCON |= I2C_I2CCON_STO;  /*Generate STOP condition*/
        HT_I2C0->I2CCON &= ~I2C_I2CCON_SI;  /* Clear the SI bit*/
    }

3.3从机发送

image-20230308132908113

void HT_I2C_Slave_Transmit(HT_I2C_TypeDef *I2Cx,uint8_t *pData, uint16_t Size)
{
    if (I2Cx==HT_I2C0)
    {
        // 1.查询I2C 控制寄存器中的中断标志位,清除中断标志
        HT_I2C0->I2CCON &= ~I2C_I2CCON_SI; /* Clear the SI bit*/    
        // 2.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
        while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}               
        
        // 3.A8H 自身SLA+R 已被接收;已返回ACK
        if(HT_I2C0->I2CSTA == I2C_SLAVE_TRANS_ADDR_ACK)             
        {
            // 4.发送数据到接收器
            while(Size >0) /* send data to slave */                 
            {
                 // 5.写入I2C 数据寄存器的值 遍历pData数组
                 HT_I2C0->I2CDAT = *(pData++);      
                 // 6.查询I2C 控制寄存器中的中断标志位,清除中断标志                
                 HT_I2C0->I2CCON &= ~I2C_I2CCON_SI;     
                 // 7.查询I2C 控制寄存器中的中断标志位,判断是否置位成功            
                 while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}    
                 // 8.B8H 数据字节已被发送;ACK 已被接收  
                 if (HT_I2C0->I2CSTA != I2C_SLAVE_TRANS_DATA_ACK)   
                 {
                    return;
                 }
            }
        }
        else return;
    }
}

3.4从机接收

image-20230308132954141

void HT_I2C_Slave_Receive(HT_I2C_TypeDef *I2Cx,uint8_t *pData, uintl6_t Size)
{
    if (I2Cx== HT_I2C0)
    {
        // 1.查询I2C 控制寄存器中的中断标志位,清除中断标志
        HT_I2C0->I2CCON &= ~I2C_I2CCON_SI; /* Clear the SI bit*/
        // 2.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
        while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}
        // 3.60H 自身的SLA+W 已被接收;已返回ACK
        if(HT_I2C0->I2CSTA == I2C_SLAVE_RCV_ADDR_ACK)
        {
            // 4.发送数据到接收器
            while(Size >0) /* send data to slave */
            {
                 // 5.写入I2C 数据寄存器的值 遍历pData数组
                 *(pData++) = HT_I2C0->I2CDAT;
                 // 6.查询I2C 控制寄存器中的中断标志位,清除中断标志
                 HT_I2C0->I2CCON &= ~I2C_I2CCON_SI;
                 // 7.查询I2C 控制寄存器中的中断标志位,判断是否置位成功
                 while(HT_I2C_ITFlagStatusGet(I2Cx)==RESET){;}
                 // 8.80H 自身SLV 地址已被寻址;DATA 字节已被接收; 已返回ACK
                 if (HT_I2C0->I2CSTA != I2C_SLAVE_RCV_DATA_ACK)
                 {
                    return;
                 }
            }
        }
        else return;
    }

}

4.中断传输

对I2C总线来说工作在中断和非中断模式在时序上是相同的,只不过在非中断模式下是通过检测ACK信号来判断从设备响应了,在中断模式下是通过中断信号来判断从设备响应了,一般是读主CPU侧的I2C控制器的中断标志来判断的。

5.Aardvark

1.aardvark适配器为发送器接口函数

# 1.设置比特率
Set Bitrate (aa_i2c_bitrate)
# 2.从 I2C 从设备读取字节流。
Master Read (aa_i2c_read)
# 3.从具有扩展状态信息的 I2C 从设备读取字节流
Master Read Extended (aa_i2c_read_ext)
# 4.向 I2C 从设备写入字节流。
Master Write (aa_i2c_write)
# 5.向具有扩展状态信息的 I2C 从设备写入字节流。
Master Write Extended (aa_i2c_write_ext)
# 6.向 I2C 从设备写入一个字节流,然后从同一个从设备读取。
Master Write-Read (aa_i2c_write_read)

2.aardvark适配器为接收器接口函数

# 1.启用 Aardvark 适配器作为 I2C 从设备。
Slave Enable (aa_i2c_slave_enable)
# 2.禁用 Aardvark 适配器作为 I2C 从设备。
Slave Disable (aa_i2c_slave_disable)
# 3.在 Aardvark 适配器进入从模式并由主服务器联系的情况下设置从服务器响应。
Slave Set Response (aa_i2c_slave_set_response)
# 4.返回从以前的 Aardvark I2C 从节写入到 I2C 主传输的字节数。
Slave Write Statistics (aa_i2c_slave_write_stats)
# 5.返回从以前的 Aardvark I2C 从服务器写入到具有扩展状态信息的 I2C 主传输的字节数。
Slave Write Statistics Extended (aa_i2c_slave_write_stats_ext)
# 6.从 I2C 从接收机读取字节。
Slave Read (aa_i2c_slave_read)
# 7.从具有扩展状态信息的 I2C 从接收机读取字节。
Slave Read Extended (aa_i2c_slave_read_ext)

3.使用方式

# 1.打开Aardvark端口
handle = aa_open(port)
if handle <= 0:
    print("Unable to open Aardvark device on port %d" % port)
    print("Error code = %d" % handle)
    sys.exit()

# 2.激活/关闭各个子系统(I2C,SPI,GPIO)
aa_configure(handle, AA_CONFIG_SPI_I2C)  

# 3.启动/关闭 SCL 和 SDA 上的 I2C 上拉电阻器
aa_i2c_pullup(handle, AA_I2C_PULLUP_BOTH)  

# Power the EEPROM using the Aardvark adapter's power supply.
# 4.激活/关闭目标电源插脚4和6
aa_target_power(handle, AA_TARGET_POWER_BOTH)  

# 5.将 I2C 比特率设置为kHz
bitrate = aa_i2c_bitrate(handle, bitrate)  
print("Bitrate set to %d kHz" % bitrate)

# Set the bus lock timeout
# 6.以毫秒为单位设置 I2C 总线锁定超时
bus_timeout = aa_i2c_bus_timeout(handle, BUS_TIMEOUT)  
print("Bus lock timeout set to %d ms" % bus_timeout)

# Perform the operation
if command == "write":
    _writeMemory(handle, device, addr, length, 0)  # 7.向 I2C 从设备写入字节流  地址、字节长度、FLAG、指向数据的指针
    print("Wrote to EEPROM")

elif command == "read":
    _readMemory(handle, device, addr, length)  # 8.从 I2C 从设备读取字节流  地址、字节长度、FLAG、指向数据的指针

信号线:

image-20230314160048660

  1. SCL
  2. GND
  3. SDA
  4. NC/+5V
  5. MISO
  6. NC/+5V
  7. SCLK
  8. MOSI
  9. SS
  10. GND

疑问:

1.为什么每次执行程序之前都要清楚中断标志?

参考文档:

(43条消息) IIC中断和非中断模式_i2c 中断模式_退5不落5的博客-CSDN博客

(43条消息) 轮询和中断的区别,中断上下文_小翅小排的博客-CSDN博客

https://www.totalphase.com/support/articles/200468316#s5.5

https://pyaardvark.readthedocs.io/en/latest/intro.html#on-pyaardvark-datatypes

I2C通信基础知识:硬件、数据传输、配置 (enroo.com)

(51条消息) STM32F767+STM32CubeMX I2C通信读写EEPROM数据(采用轮询、DMA、中断三种方式)_stm32cubemx配置iic总线读写eeprom代码_LI++的博客-CSDN博客

(61条消息) Arduino UNO向AT24C02写入数据IIC完整通讯过程详解_at24c02写数据流程图_perseverance52的博客-CSDN博客

(61条消息) I2C通信协议详解-CSDN博客


http://www.kler.cn/a/2477.html

相关文章:

  • 怿星科技联合赛力斯举办workshop活动,进一步推动双方合作
  • 【图像分类实用脚本】数据可视化以及高数量类别截断
  • 三、ubuntu18.04安装docker
  • flutter小tip—— initState 和 build(一)
  • 【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容
  • 图像根据mask拼接时,边缘有色差 解决
  • Linux系统下gdb调试
  • 【Go】K8s 管理系统项目[Jenkins Pipeline K8s环境–应用部署]
  • Python 项目之实现文件内容的反转再输入(一)完全反转
  • react中渲染企业微信的表情
  • 使用shell 脚本,批量解压一批zip文件,解压后的文件放在以原zip文件名前10个字符的文件夹中的例子
  • Java stream性能比较
  • java基础面试题(四)
  • TU-95 strategic bomber气动布局分析
  • 蓝桥杯训练day3
  • 深入理解JVM虚拟机(六)
  • 梳理LVM逻辑卷管理,
  • JDBC
  • C++虚函数与多态
  • ChatGPT推出第四代GPT-4!不仅能聊天,还可以图片创作!
  • 【图神经网络 文献精读】针对SARS-CoV-2大流行的改进和优化的药物再利用方案
  • 生成时序异常样本-学习记录-未完待续
  • 毕业设计 基于51单片机自动智能浇花系统设计
  • 【十二天学java】day05--数组和循环高级
  • 01 | Msyql系统架构
  • 学习系统编程No.7【进程替换】