1、I2C总线相关概念的介绍

1> I2C总线是PHLIPS公司在八十年代初推出的一种同步串行的半双工总线,主要用于连接整体电路。

2> I2C总线为两线制,只有两根双向信号线。一根是数据线SDA,另一根是 时钟线SCL

3> I2C硬件结构简单,接口连接方便,成本较低。因此在各个领域得到了广泛的应用。

​ 传感器:温湿度传感器,环境光接近传感器,心率和脉搏传感器,触摸屏传感器。

​ EEPROM,DS1302, ADC芯片,

4> I2C总线上需要外接两个上拉电阻,上拉电阻的作用是再空闲态时,保持总线为高电平的状态

2、I2C总线的硬件连接

1> I2C是具备多主机多从机系统所需的包括总线裁决功能的高性能串行总线。

2> 在同一时刻只能由一个主机一个从机使用I2C总线

3> 每个接到I2C总线上的器件都有唯一的从机地址。

4> 主机与其它器件进行数据传送时总线上发送数据的器件为发送器,总线上接收数据的器件则为接收器。

5> 可以主动发起通信的叫做主机(Master),只能被动收发数据的叫做从机(Slave)。

3、I2C总线的时序图分析

时序图:随着时钟信号的变化,数据线上的数据的变化的图形就叫做时序图。

3.1 起始信号和终止信号时序图

1> SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;

2> SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号;

3> 起始和终止信号都是由主机发出,起始信号产生后,总线就处于占用的态;终止信号产生后,总线就处于空闲态。

3.2 数据信号时序图

1> I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,

​ 只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

2> 时钟在低电平期间,发送器向数据线上写入数据,此时允许数据线上的数据变化;

3> 时钟在高电平期间,接收器从数据线上读取数据,此时必须保持数据线上的数据稳定;

4> 一个时钟周期完成一个bit位数据的收发。

3.3 应答信号时序图

1> I2C总线采用了应答的机制,接收器收到1个字节的数据之后,必须给发送器返回应答信号。

2> 每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),在发送低位,

​ 每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

3> 在第九个时钟周期的低电平期间,接收器向数据线上写入数据,

​ 在第9个时钟周期的高电平期间,发送器从数据线上读取数据,

​ 如果读到的是低电平,表示应答信号,如果读到的是高电平,表示非应答信号。

3.4 从机地址

1> I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。

2> 主机在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/W),用“0”表示主机发送数据(W),“1”表示主机接收数据(R)。

3> 总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己被主机寻址,根据R/W位将自己定为发送器或接收器。

4、IIC总线的硬件连接

5、IIC总线的通信协议

5.1 主机给从机写一个字节的通信协议

5.2 主机给从机发送连续的多个字节的通信协议

5.3 主机从从机读一个字节的通信协议

5.4 主机从从机读连续的多个字节的通信协议

从机只能被动的收发数据,当从机给主机发送数据时,如果主机给从机发送的是应答信号,则从机会认为主机还想接收下一个字节的数据,因此从机会给主机发送下一个字节的数据。如果从机收到的是非应答信号,则从机不会发送下一个字节的数据给主机。

6分析手册(这个略,每个芯片手册都不一样,具体解析都再代码实现中有详细写到)

7代码实现

#include "../include/iic.h"extern void printf(const char *fmt, ...);
/** 函数名 : delay_us* 函数功能:延时函数* 函数参数:无* 函数返回值:无* */
void delay_us(void)
{unsigned int i = 2000;while (i--);
}
/** 函数名 : i2c_init* 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,* 函数参数:无* 函数返回值:无* */
void i2c_init(void)
{// 使能GPIOF端口的时钟RCC->MP_AHB4ENSETR |= (0x1 << 5);// 设置PF14,PF15引脚为通用的输出功能GPIOF->MODER &= (~(0xF << 28));GPIOF->MODER |= (0x5 << 28);// 设置PF14, PF15引脚为推挽输出GPIOF->OTYPER &= (~(0x3 << 14));// 设置PF14, PF15引脚为高速输出GPIOF->OSPEEDR |= (0xF << 28);// 设置PF14, PF15引脚的禁止上拉和下拉GPIOF->PUPDR &= (~(0xF << 28));// 空闲状态SDA和SCL拉高I2C_SCL_H;I2C_SDA_H;
}/** 函数名:i2c_start* 函数功能:模拟i2c开始信号的时序* 函数参数:无* 函数返回值:无* */
void i2c_start(void)
{/** 开始信号:时钟在高电平期间,数据线从高到低的变化*     --------* SCL         \*              --------*     ----* SDA     \*          --------* */SET_SDA_OUT;   // 设置SDA为输出模式,确保一定为输出模式I2C_SDA_H;     // SDA拉高I2C_SCL_H;     // SCL拉高delay_us();I2C_SDA_L;     // SDA拉低delay_us();I2C_SCL_L;     // SCL拉低,I2C总线就处于占用态
}/** 函数名:i2c_stop* 函数功能:模拟i2c停止信号的时序* 函数参数:无* 函数返回值:无* */void i2c_stop(void)
{/** 停止信号 : 时钟在高电平期间,数据线从低到高的变化*             ----------* SCL        /*    --------*    ---         -------* SDA   X       /*    --- -------*    为了确保停止信号是一个上升沿,因此在时钟为低电平期间*    将数据线拉低,确保可以产生上升沿。* */SET_SDA_OUT;    // 设置SDA为输出I2C_SCL_L;      // SCL拉低delay_us();I2C_SDA_L;      // SDA拉低delay_us();I2C_SCL_H;      // SCL拉高delay_us();I2C_SDA_H;      // SDA拉高}/** 函数名: i2c_write_byte* 函数功能:主机向i2c总线上的从设备写8bits数据* 函数参数:dat : 等待发送的字节数据* 函数返回值: 无* */void i2c_write_byte(unsigned char dat)
{/** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据*      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*    SDA         X                  X*      -------- ------------------ ---*      先发送高位在发送低位* */unsigned int i;SET_SDA_OUT;   // SDA为输出for (i = 0 ; i < 8; i++) {I2C_SCL_L;   // SCL拉低delay_us();if (dat & 0x80)  // 判断最高位的值I2C_SDA_H;   // 向数据线上写入高电平else I2C_SDA_L;   // 向数据线上写入低电平delay_us();I2C_SCL_H;   // SCL拉高delay_us();delay_us();  // 等待从机将数据线上的数据读走dat <<= 1;   // 将次高位的数据移动到最高位}
}/** 函数名:i2c_read_byte* 函数功能: 主机从i2c总线上的从设备读8bits数据,*          主机发送一个应答或者非应答信号给从机* 函数参数: 0 : 应答信号   1 : 非应答信号* 函数返回值:读到的有效数据** */
unsigned char i2c_read_byte(unsigned char ack)
{/** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据*      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*    SDA         X                  X*      -------- ------------------ ---**      先接收高位, 在接收低位* */unsigned char dat;unsigned int i;SET_SDA_IN;   // 设置SDA为输入for (i = 0; i < 8; i++) {I2C_SCL_L;   // SCL拉低delay_us();delay_us();  // 等待从机向数据线上写入数据I2C_SCL_H;   // SCL拉高delay_us();dat <<= 1;if (I2C_SDA_READ) dat |= 1;else dat |= 0;delay_us();}if (!ack) i2c_ack();   // 发送应答信号else i2c_nack();  // 发送非应答信号return dat;}
/*将dat中数据的左移1位写到接收数据的上边。为什么将dat中的数据左移1位,写道接收数据的下边不可以?假设接收的数据为0xFF,先接收数据    在进行左移第1次循环        0000 0001    0000 0010第2次循环        0000 0011    0000 0110第3次循环        0000 0111    0000 1110第4次循环        0000 1111    0001 1110第5次循环        0001 1111    0011 1110第6次循环        0011 1111    0111 1110第7次循环        0111 1111    1111 1110第8次循环        1111 1111    1111 1110*//** 函数名: i2c_wait_ack* 函数功能: 主机作为发送器时,等待接收器返回的应答信号* 函数参数:无* 函数返回值:*                  0:接收到的应答信号*                  1:接收到的非应答信号* */
unsigned char i2c_wait_ack(void)
{int ack;/** 主机发送一个字节之后,从机给主机返回一个应答信号,主机接收应答信号*                       -----------* SCL                  /   M:读    \*     -----------------             --------*     --- -------- --------------------* SDA    X        X*     ---          --------------------*     主  释 设  从机    主机*     机  放 置  向数据  读数据线*         总 SDA 线写    上的数据*         线 输  数据*            入* */I2C_SCL_L;   // SCL拉低delay_us();I2C_SDA_H;   // SDA拉高, 释放总线SET_SDA_IN;  // 设置SDA为输入delay_us();delay_us();  // 等待从机向数据线上写入应答信号I2C_SCL_H;   // SCL拉高delay_us();if (I2C_SDA_READ) ack = 1;  // 非应答信号else ack = 0;  // 应答信号delay_us();I2C_SCL_L;return ack;
}
/** 函数名: iic_ack* 函数功能: 主机作为接收器时,给发送器发送应答信号* 函数参数:无* 函数返回值:无* */
void i2c_ack(void)
{/*            --------* SCL       /        \*    -------          ------*    ---* SDA   X*    --- -------------*    在第九个时钟周期的低电平期间,接收器向数据线写入数据,*    在第九个时钟周期的高电平期间,发送器从数据线上读取数据,*    如果读到低电平表示应答信号* */SET_SDA_OUT;   // 设置SDA为输出I2C_SCL_L;     // SCL拉低delay_us();I2C_SDA_L;     // SDA拉低,发送的是应答信号delay_us();   I2C_SCL_H;     // SCL拉高delay_us();delay_us();    // 等待从机接收应答信号I2C_SCL_L;     // SCL拉低
}
/** 函数名: iic_nack* 函数功能: 主机作为接收器时,给发送器发送非应答信号* 函数参数:无* 函数返回值:无* */
void i2c_nack(void)
{/*            --------* SCL       /        \*    -------          ------*    --- ---------------* SDA   X*    ---*    在第九个时钟周期的低电平期间,接收器向数据线写入数据,*    在第九个时钟周期的高电平期间,发送器从数据线上读取数据,*    如果读到高电平表示非应答信号* */SET_SDA_OUT;   // 设置SDA为输出I2C_SCL_L;     // SCL拉低delay_us();I2C_SDA_H;     // SDA拉高,发送的是非应答信号delay_us();   I2C_SCL_H;     // SCL拉高delay_us();delay_us();    // 等待从机接收非应答信号I2C_SCL_L;     // SCL拉低
}

IIC/I2C总线实验相关推荐

  1. IIC(I2C)总线上的设备其地址如何定义?通俗易懂

    IIC(I2C)总线上的设备其地址如何定义? IIC总线 一般串行数据通讯都有时钟和数据之分,有异步和同步之别. 有单线,双线和三线等. I2C肯定是2线的(不算地线). I2C协议确实很科学,比3/ ...

  2. DSP 2803x IIC/I2C总线

    参考手册:TMS320x2803x, Piccolo Technical Reference Manual.pdf IIC总线: 概念 IIC总线(Inter IC BUS)由 PHILIPS 公司推 ...

  3. IIC(I2C)总线设备地址,例如E2PROM、CH455G等器件应用

    文章目录 背景 I2C特点 IIC的物理层 IIC的协议层 a.数据的有效性 b.起始和结束条件 d.数据帧格式 地址格式(重点!!!) 实战寻址 E2PROM 注意 CH455G地址 注意 背景 I ...

  4. 浅谈 IIC I2C 总线协议

    简介 IIC(也称I2C或I2C)总线是Philips公司开发的一种简单.双向二线制同步串行总线,是Inter-Integrated Circuit的缩写. IIC只用两条双向线,一条SDA(Seri ...

  5. I2C总线及AT24C02读写实验报告

    /************************************************** 实验名称:I2C总线实验一 实验目的:利用定时器产生0~99秒表在数码管前两位显示 每过一秒写入 ...

  6. 树莓派基础实验29:I2C LCD1602实验

    一.介绍    众所周知,虽然液晶显示器和其他显示器大大的丰富了人机交互,但他们有一个共同的弱点.当它们连接到控制器时,需要占用大量的IO口,但是一般的控制器没有那么多的外部端口,也限制了控制器的其他 ...

  7. I2C总线之(三)---以C语言理解IIC

    为了加深对I2C总线的理解,用C语言模拟IIC总线,边看源代码边读波形: 如下图所示的写操作的时序图: 读时序的理解同理.对于时序不理解的朋友请参考"I2C总线之(二)---时序" ...

  8. linux下的iic驱动程序,实战经验吐血推荐:怎样在Linux环境下轻松实现基于I2C总线的EEPROM驱动程序...

    4开发实例本文引用地址:http://www.eepw.com.cn/article/279351.htm Linux内核已经提供了I2C驱动中所需要的基本模块.i2c-core.i2c-dev和i2 ...

  9. i.MX6ULL终结者Linux I2C驱动实验IMX6ULL的I2C总线驱动分析

    在上一节中我们了解了I2C框架分为I2C核心.I2C总线驱动和I2C设备驱动三部分.其中I2C总线驱动就是SOC的I2C控制器驱动,一般来说都是SOC厂家实现好的.而I2C设备驱动是用户根据自己不同的 ...

最新文章

  1. Java快速判断是不是手机号
  2. php 文件保存函数,PHP文件函数
  3. IDEA写vue项目出现红色波浪线警告如何解决??
  4. jwt如何防止token被窃取_在吗?认识一下JWT(JSON Web Token)?
  5. Oracle视图添加约束,Oracle创建视图的语法
  6. Exchange Online基于网络位置限制使用
  7. Android中AMS工作原理,Android AMS启动详解
  8. iOS 控件宽高字体大小适配方法
  9. [文摘20070816]家(周国平)
  10. 【分享】一个电路仿真软件---proteus ISIS
  11. CentOS 、Linux 配置国内阿里源、yum源镜像
  12. mysql添加表字段脚本_mysql数据库修改字段及新增字段脚本
  13. 表格求和怎么操作?如何运用sumif函数进行求和
  14. 两高一部电子数据取证规则_“鼓浪听涛,论道取证”2019中国电子数据取证峰会在厦门召开,行业专家齐聚...
  15. 考研日记2021年9月16日
  16. 一款适用甲方企业的外网资产周期性扫描监控系统
  17. 《城市化》(顾朝林)-重要术语
  18. Debian 11 修改 DNS 服务器
  19. 面试官:你对MySQL中的索引了解多少?
  20. 华为OD机试 - 租车骑绿岛

热门文章

  1. Linux syslog进程退出日志审计
  2. 回归算法-线性回归分析-过拟合欠拟合岭回归
  3. 云计算学习1——OpenStack云计算安装部署步骤图文并茂(先电2.2)
  4. 库函数 qsort 的用法
  5. GLUT的下载与安装
  6. 二维码如何转为链接,看了这篇文章才知道多好用!
  7. QQ群课堂无法打开和加入
  8. 从2010 IDF 看英特尔固态硬盘现状及策略
  9. MySQL启动失败,试图访问许可验证文件时出错,请重新安装SQL Server来更正次文件
  10. Presto RBO之broadcast join与partitioned join类型的选择优化