I2C总线的通信过程(见图4-8)主要包含三个主要阶段:起始阶段、数据传输阶段和终止阶段。

1. 起始阶段

在I2C总线不工作的情况下,SDA(数据线)和SCL(时钟线)上的信号均为高电平。如果此时主机需要发起新的通信请求,那么需要首先通过SDA和SCL发出起始标志。当SCL为高电平时,SDA电平从高变低,这一变化表示完成了通信的起始条件。

在起始条件和数据通信之间,通常会有延时要求,具体的指标会在设备厂商的规格说明书中给出。

2. 数据传输阶段

I2C总线的数据通信是以字节(8位)作为基本单位在SDA上进行串行传输的。一个字节的传输需要9个时钟周期。其中,字节中每一位的传输都需要一个时钟周期,当新的SCL到来时,SCL为低电平,此时数据发送方根据当前传输的数据位控制SDA的电平信号。如果传输的数据位为"1",就将SDA电平拉高;如果传输的数据位为"0",就将SDA的电平拉低。当SDA上的数据准备好之后,SCL由低变高,此时数据接收方将会在下一次SCL信号变低之前完成数据的接收。当8位数据发送完成后,数据接收方需要一个时钟周期以使用SDA发送ACK信号,表明数据是否接收成功。当ACK信号为"0"时,说明接收成功;为"1"时,说明接收失败。每个字节的传输都是由高位(MSB)到低位(LSB)依次进行传输。

I2C总线协议中规定,数据通信的第一个字节必须由主机发出,内容为此次通信的目标设备地址和数据通信的方向(读/写)。在这个字节中,第1~7位为目标设备地址,第0位为通信方向,当第0位为"1"时表示读,即后续的数据由目标设备发出主机进行接收;当第0位为"0"时表示写,即后续的数据由主机发出目标设备进行接收。在数据通信过程中,总是由数据接收方发出ACK信号。

3. 终止阶段

当主机完成数据通信,并终止本次传输时会发出终止信号。当SCL 是高电平时,SDA电平由低变高,这个变化意味着传输终止。

下面给出了模拟I2C总线进行读写的伪代码,用以说明如何使用GPIO实现I2C通信:

#define SDA 254                         //定义SDA所对应的GPIO接口编号
#define SCL 255                         //定义SCL所对应的GPIO接口编号
#define OUTP 1                          //表示GPIO接口方向为输出
#define INP 0                           //表示GPIO接口方向为输入
/* I2C起始条件 */
int i2c_start()
{
//初始化GPIO口
set_gpio_direction(SDA, OUTP);          //设置SDA方向为输出
set_gpio_direction (SCL, OUTP);         //设置SCL方向为输出  set_gpio_value(SDA, 1);                //设置SDA为高电平
set_gpio_value(SCL, 1);                 //设置SCL为高电平
delay();                            //延时
//起始条件
set_gpio_value(SDA, 0);                 //SCL为高电平时,SDA由高变低
delay();
}
/* I2C终止条件 */
void i2c_stop()
{
set_gpio_value(SCL, 1);
set_gpio_direction(SDA, OUTP);
set_gpio_value(SDA, 0);
delay();
set_gpio_value(SDA, 1);             //SCL高电平时,SDA由低变高
}
/*
I2C读取ACK信号(写数据时使用)
返回值 :0表示ACK信号有效;非0表示ACK信号无效
*/
unsigned char i2c_read_ack()
{
unsigned char r;
set_gpio_direction(SDA, INP);           //设置SDA方向为输入
set_gpio_value(SCL,0);              // SCL变低
r = get_gpio_value(SDA);                //读取ACK信号
delay();
set_gpio_value(SCL,1);              // SCL变高
delay();
return r;
}
/* I2C发出ACK信号(读数据时使用) */
int i2c_send_ack()
{
set_gpio_direction(SDA, OUTP);          //设置SDA方向为输出
set_gpio_value(SCL,0);              // SCL变低
set_gpio_value(SDA, 0);             //发出ACK信号
delay();
set_gpio_value(SCL,1);              // SCL变高
delay();
}
/* I2C字节写 */
void i2c_write_byte(unsigned char b)
{
int i;
set_gpio_direction(SDA, OUTP);          //设置SDA方向为输出
for (i=7; i>=0; i--) {
set_gpio_value(SCL, 0);             // SCL变低
delay();
set_gpio_value(SDA, b & (1<<i));        //从高位到低位依次准备数据进行发送
set_gpio_value(SCL, 1);             // SCL变高
delay();
}
i2c_read_ack();                 //检查目标设备的ACK信号
}
/* I2C字节读 */
unsigned char i2c_read_byte()
{
int i;
unsigned char r = 0;
set_gpio_direction(SDA, INP);           //设置SDA方向为输入
for (i=7; i>=0; i--) {
set_gpio_value(SCL, 0);         // SCL变低
delay();
r = (r <<1) | get_gpio_value(SDA);      //从高位到低位依次准备数据进行读取
set_gpio_value(SCL, 1);         // SCL变高
delay();
}
i2c_send_ack();                 //向目标设备发送ACK信号
return r;
}
/*
I2C读操作
addr:目标设备地址
buf:读缓冲区
len:读入字节的长度
*/
void i2c_read(unsigned char addr, unsigned char* buf, int len)
{
int i;
unsigned char t;
i2c_start();                        //起始条件,开始数据通信
//发送地址和数据读写方向
t = (addr << 1) | 1;                    //低位为1,表示读数据
i2c_write_byte(t);
//读入数据
for (i=0; i<len; i++)
buf[i] = i2c_read_byte();
i2c_stop();                     //终止条件,结束数据通信
}
/*
I2C写操作
addr:目标设备地址
buf:写缓冲区
len:写入字节的长度
*/
void i2c_write (unsigned char addr, unsigned char* buf, int len)
{
int i;
unsigned char t;
i2c_start();                        //起始条件,开始数据通信
//发送地址和数据读写方向
t = (addr << 1) | 0;                    //低位为0,表示写数据
i2c_write_byte(t);
//写入数据
for (i=0; i<len; i++)
i2c_write_byte(buf[i]);
i2c_stop();                     //终止条件,结束数据通信
}  

使用GPIO模拟I2C总线进行通信相关推荐

  1. Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  2. MTK6592 GPIO模拟I2C

    虽然Linux内核支持该功能,但是MTK并不支持I2C-GPIO功能,官方说法是硬件I2C接口已经够多,不需要软件模拟,并且用GPIO模拟I2C会影响系统性能... 所以,要做的,就是将被MTK阉割的 ...

  3. SylixOS 基于STM32平台的GPIO模仿I2C总线的驱动开发流程

    2019独角兽企业重金招聘Python工程师标准>>> 概述 本文档以STM32F767平台为例,详细介绍SylixOS上GPIO模仿I2C总线的驱动开发流程. 初始化 GPIO模仿 ...

  4. S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动

    目录:一. 说明 二. 驱动程序说明及问题 三. 案例一       四. 案例二 一. 说明 mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看 ...

  5. 外设驱动库开发笔记8:GPIO模拟I2C驱动

    I2C总线简单方便,是我们经常使用的一种总线.但有时候我们的MCU没有足够多的I2C控制器来实现我们的应用,所幸我可以使用普通的GPIO引脚来模拟低速的I2C总线通信.这一节我们就来实现使用软件通过普 ...

  6. STM32 通用GPIO模拟I2C实现

    STM32 通用GPIO模拟I2C实现 通用GPIO模拟I2C通信实现样例 1 GPIO初始化 #ifdef HW_I2C1 //硬件I2C初始化//PA8-I2C1_SCLGPIO_StructIn ...

  7. i2c 驱动五:gpio模拟i2c

    有关linux的i2c相关文章有一下几篇,他们互相关联,应该一同看: - i2c 驱动一:简介 - i2c 驱动二:devfs文件系统 - i2c 驱动三:自己实现设备和驱动分离 - i2c 驱动四: ...

  8. GPIO模拟I2C程序实现

    GPIO模拟I2C程序实现. I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA).I2C是总线结构,1个 ...

  9. linux源码gpio模拟i2c,linux内核gpio模拟i2c实例.doc

    linux内核gpio模拟i2c实例.doc linux内核GPIO模拟I2C实例2010-10-11作者:cvip302814来源:cvip302814的blog前言:在许多情况下,我们并没有足够的 ...

最新文章

  1. C++中的虚函数表介绍
  2. 人在职场,干得好真的不如汇报得好?
  3. cad填充图案乱理石_CAD软件中如何自定义CAD填充图案?
  4. 【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?
  5. 解读自监督学习(Self-Supervised Learning)几篇相关paper -
  6. classpath是什么
  7. unity 引用prefab_Unity基础教程-对象管理(二)——对象多样化(Fabricating Shapes)...
  8. 凸优化第三章凸函数 3.6 关于广义不等式的凸性
  9. Matlab 2016a 安装及破解教程
  10. 驱动人生安装驱动计算机无法启动,驱动人生怎么安装驱动程序?驱动人生基本功能...
  11. 最是人间四月天,细雨点洒樱花前
  12. 第09课:一网打尽神经序列模型之 RNN 及其变种 LSTM、GRU
  13. 计算机配置内存在哪看,从哪里看电脑配置
  14. 赠书 | 《网络威胁情报技术指南》
  15. 移动端的vm vh是什么
  16. 【Ajax+】爬去今日头条图片
  17. java实现xml的xsd验证_Java通过XML Schema校验XML
  18. 图层的混合模式全解析-Photoshop照片处理叠图基础教程
  19. 语法分析:自上而下分析(递归下降分析法+预测分析法)
  20. html 组件化 编辑器,SpreadJS V14.0发布:组件化编辑器+数据透视表

热门文章

  1. 积木赛尔号机器人_精灵伙伴帮你打怪?我的世界x赛尔号联动即将开启!
  2. python自带模块连接数据库_Python模块Sqlite数据库模块
  3. pymysql安装_openstack stein安装placement
  4. Windows IIS配置Jsp和php环境方法
  5. 10-Flink集群的高可用(搭建篇补充)
  6. SSIS同步多个数据库
  7. Think Python - Chapter 12 Tuples
  8. 软件工程——理论、方法与实践⑨
  9. [置顶] 软件设计之道_读书纪要.doc
  10. iOS toolchain based on clang for linux