一、关于i2c协议概述

I2C总线协议只需要2根信号线即可完成数据的传输,这两根线分别是时钟线SCL和信号线SDA。I2C线上有且只有1个主设备Master和若干个从设备Slave,区别Master和Slave的标准是SCL,即谁是SCL的提供者,谁就是Master,而与SDA无关。这点尤其需要注意,发送SDA不能作为区别Master和Slave的标准。

关于I2C总线再作以下说明:
1-两条总线SDA和SCL都必须接上拉电阻,这是为了确保两条总线在空闲时都是高电平,上拉电阻的经典取值是10kΩ;
2-I2C总线上可以挂载多个主机和多个从机,但是同一时间只允许1个主机和1个从机进行通信;
3-总线上每一个设备都有一个独立的地址,通过该地址实现通信;
4-总线上的设备是“线与”的关系,即任一设备的管脚输出低电平都可以将该管脚所在总线的电平拉低,线与关系是时钟同步和总线仲裁的硬件基础;
5-I2C传输速度有标准速度模式(SS Mode)、快速模式(FS Mode)和高速模式(HS Mode)三种,数据传输速率分别为100kbps、400kbps和3.4Mbps。

协议层:协议层规定了通讯的起始停止信号、数据有效性、响应、总线仲裁、时钟同步、地址广播等内容。

1、总线空闲与信号起始终止
   I2C协议规定SDA和SCL都为高电平时总线空闲(not busy)。

I2C协议规定SCL保持高电平、SDA由高变低为起始信号(start),所有命令和数据的传输必须以起始信号为首。

I2C协议规定SCL保持高电平、SDA由低变高为终止信号(stop)。所有命令和数据的传输必须以终止信号为尾。

2、数据有效
I2C协议规定在总线上出现起始信号start后,若SCL在高电平期间SDA保持电平不变,则SDA的状态表示有效数据(data valid)。在传输数据时SDA的改变必须只能发生在SCL为
低电平期间,每一bit数据有1个时钟脉冲时长。

3、应答和非应答
   I2C协议规定每个被寻址设备在接收1字节数据后都必须向发送字节的设备发送应答(ACK)信号,确认的器件必须在应答时钟脉冲期间下拉SDA线,使得SDA线在应答相关时钟脉冲
SCL为高电平期间稳定为低电平。

I2C协议规定与ACK信号相反的信号为非应答(not ACK)信号。在主器件从从器件中读取数据时,主器件必须在读取的最后1字节数据后在SDA总线上产生not ACK信号以示意从器
件停止发送数据。not ACK信号是在SCL为高电平期间保持SDA也为高电平。

4、地址广播
地址广播是I2C协议规定的寻址方式。它是指主设备在产生start信号后,各个从设备开始关注总线SDA信号,此时主设备在总线上生成需接受/发送数据的从设备的
地址(Address),相当于向总线上所有从设备广播了这一地址。每个从设备将总线上的地址与自己的地址相对比,不一致的退出接收,一致的继续接收,直到8bit地址数据
广播完毕,仍然留下的那一个从设备就是主设备的寻址目标。

5、总线仲裁解决的是多个主设备竞争使用同一总线的问题。

假设主控器1要发送的数据DATA1为“101 ……”;主控器2要发送的数据DATA2为“1001 ……”总线被启动后两个主控器在每发送一个数据位时都要对自己的输出电平进行检测,
只要检测的电平与自己发出的电平一致,他们就会继续占用总线。在这种情况下总线还是得不到仲裁。当主控器1发送第3位数据“1”时(主控器2发送“0” ),由于“线与”的
结果SDA上的电平为“0”,这样当主控器1检测自己的输出电平时,就会测到一个与自身不相符的“0”电平。这时主控器1只好放弃对总线的控制权;因此主控器2就成为总线的
唯一主宰者。(实例来自博客)

从中可以得出:参与仲裁的所有主控器都不会丢失数据;参与仲裁的所有主控器没有固定的优先级别,而是遵循低电平优先的原则。

6、时钟同步

时钟同步是用来解决中控器和被控器的数据传输速率不相同的问题。

被控器可以通过将SCL主动拉低并延长其低电平时间的方法来通知主控器,当主控器在准备下一次传送时发现SCL为低电平,就会等待,直至被控器完成操作并释放SCL线的
控制控制权。这样,主控器实际上受到被控器的时钟同步控制。由此可见,SCL线上的低电平是由时钟低电平最长的器件决定,高电平的时间由高电平时间最短的器件决定。

需要说明的是,不管是总线仲裁还是时钟同步,它们得以实现的基础是SDA总线的“线与”性质,而这是由I2C总线独特的IO结构决定的。另外,总线仲裁和时钟同步之间并
不存在特定的先后关系,它们往往同时发生。

二、GPIO模拟I2C协议的C代码实现

下面将用C语言实现上面所描述的I2C总线协议的各个动作,并将这些分散的动作整合起来实现字节的读写操作。

1、首先需要定义2个IO口以连接两根总线SDA和SC,例如我这里使用250和251,然后定义i2c速率,即delay的值,这里设置为10k,即udelay(100);

我们可以通过以下两个命令来查看确认自己需要使用的gpio的数字是多少(其他平台可能节点不同,不过名称基本上是pinmux-pins),注意结合硬件原理图或者跟硬件工程师确认:
cat /d/pinctrl/pinctrl/pinmux-pins 或者 cat /sys/kernel/debug/pinctrl/pinctrl/pinmux-pins

cat /sys/kernel/debug/gpio //查看GPIO当前的申请使用情况和当前的电平状态

#define DELAY_TIME_HXD 100 //设置i2c clk, hxd019大概要求10k左右
#define GPIO_SDA 250
#define GPIO_SCL 251

2、初始化gpio模拟的i2c通讯SDA和SCL, 拉高SDA和SCL,初始化的效果是SDA和SCL总线上全部呈现高电平

static int gpio_i2c_init(void)
{int ret;if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);if (!gpio_is_valid(GPIO_SDA)) {printk("GPIO_SDA: %d is invalid\n", GPIO_SDA);return -ENODEV;}ret = gpio_request(GPIO_SDA,"GPIO_SDA");if(ret < 0){printk("request GPIO_SDA error\n");return ret;}if (!gpio_is_valid(GPIO_SCL)) {printk("GPIO_SCL: %d is invalid\n", GPIO_SCL);return -ENODEV;}ret = gpio_request(GPIO_SCL,"GPIO_SCL");if(ret < 0){    printk("request GPIO_SCL error\n");return ret;}if (!gpio_is_valid(GPIO_BUSY)) {printk("GPIO_BUSY: %d is invalid\n", GPIO_BUSY);return -ENODEV;}ret = gpio_request(GPIO_BUSY,"GPIO_BUSY");if(ret < 0){    printk("request GPIO_BUSY error\n");return ret;}ret = gpio_direction_input(GPIO_BUSY); // set GPIO_BUSY as input IOif(ret < 0){printk("set GPIO_BUSY direction fail");return ret;}gpio_direction_output(GPIO_SDA, 1);gpio_direction_output(GPIO_SCL, 1);return 0;}

3、起始信号,用GPIO模拟起始信号,SCL保持高电平、SDA由高变低。

static int iic_i2cstart_hxd019(void)
{if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);gpio_direction_output(GPIO_SDA, 1);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SDA, 0);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);return 0;}

4、终止信号:用GPIO模拟起始信号,SCL保持高电平、SDA由低变高。

static int iic_i2cstop_hxd019(void)
{if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);gpio_direction_output(GPIO_SDA, 0);gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SDA, 1);udelay(DELAY_TIME_HXD);return 0;}

5、主控读取ack应答信号,即主控平台读取ACK对应的IO口处的电平。

uint8_t GetACKSign_hxd019(void)  //主控器读取ACK对应的IO口处的电平
{uint8_t ACKSign;if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);gpio_direction_input(GPIO_SDA);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);ACKSign = gpio_get_value(GPIO_SDA);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);return ACKSign;
}

6、主控发送ack信号,即主控平台向ACK对应的IO口发送低电平。

void SendACKSign_hxd019(void) //主控器发送ACK
{if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SDA, 0);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 0);  udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);return ;
}

7、主控发送not ACK信号,即主控平台向ACK对应的IO口发送高电平。

void SendNoACKSign_hxd019(void) //主控器发送not ack
{if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 0);return ;
}

8、单字节读操作,它的逻辑是初始化—主控器发送起始信号—主控器逐bit读取SDA线上信号。

static int iic_I2CReadData_hxd019(uint8_t* pbData,int num) //单字节读操作
{uint8_t readdata = 0;int i=8;gpio_direction_input(GPIO_SDA);while (i--){readdata <<= 1;gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);readdata |= gpio_get_value(GPIO_SDA);gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);}gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);*pbData = readdata;if(num >= (ReadBytesTotal -1))SendNoACKSign_hxd019();elseSendACKSign_hxd019();udelay(DELAY_TIME_HXD);udelay(DELAY_TIME_HXD);return 0;
}

9、单字节写操作,它的逻辑是初始化—主控器发送起始信号—主控器逐bit往SDA线上写数据。

static uint8_t iic_I2CWriteData_hxd019(uint8_t bData) //单字节写操作
{uint8_t Data_Bit,ACKSign;int i = 0;if(hxd019d_dbg_en == 1)printk("hxd019d: %s, line(%d)\n", __func__, __LINE__);gpio_direction_output(GPIO_SCL, 0);udelay(DELAY_TIME_HXD);for(i = 7;i >= 0; i--){udelay(DELAY_TIME_HXD);Data_Bit= (bData >> i) & 0x01;if(Data_Bit)gpio_direction_output(GPIO_SDA, 1);elsegpio_direction_output(GPIO_SDA, 0);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 1);udelay(DELAY_TIME_HXD);gpio_direction_output(GPIO_SCL, 0);}ACKSign=GetACKSign_hxd019();return ACKSign;     }

10、主控检查是否接收到ACK,主控器在发完第1个地址字节后,按规定被控期需要向主控器回复一个ACK信号,
主控器如果能在总线上检测到这个ACK,就继续向被控器传送数据,否则视为本次数据传送失败。

uint8_t i2c_ack_check(uint8_t ctrl_byte)
{iic_i2cstart_hxd019();iic_I2CWriteData_hxd019(ctrl_byte);if(GetACKSign_hxd019() == 0){// time delay here is not necessary, just to make waveforms more readableudelay(DELAY_TIME_HXD);gpio_direction_input(GPIO_SDA);        // set SDA as inputgpio_direction_input(GPIO_SCL);      // set SCL as inputreturn 0;}else{// time delay here is to save computing resourceudelay(DELAY_TIME_HXD);return 1;}
}

11、i2c读写寄存器操作

int gpio_analog_i2c_read(unsigned char I2cRegAddr)//读寄存器操作
{int i = 100;//spin_lock(&a_lock);unsigned char bValue = 0;unsigned char devAddr = 0;if(hxd019d_dbg_en == 1)printk("hxd019d: ------%s(%d)-------\n", __func__, __LINE__);devAddr = (chipSlaveAddr << 1);iic_i2copen_hxd019();udelay(DELAY_TIME_HXD);iic_i2cstart_hxd019();udelay(DELAY_TIME_HXD);iic_I2CWriteData_hxd019(devAddr);udelay(DELAY_TIME_HXD);iic_I2CWriteData_hxd019(I2cRegAddr);udelay(DELAY_TIME_HXD);iic_i2cstart_hxd019();udelay(DELAY_TIME_HXD);iic_I2CWriteData_hxd019((devAddr | 0x1));udelay(DELAY_TIME_HXD);iic_I2CReadData_hxd019(&bValue,i);udelay(DELAY_TIME_HXD);iic_i2cstop_hxd019();udelay(DELAY_TIME_HXD);iic_i2cclose_hxd019();udelay(DELAY_TIME_HXD);//spin_unlock(&a_lock);return bValue;  }int gpio_analog_i2c_write(unsigned char I2cRegAddr,unsigned char data) //写寄存器操作
{//spin_lock(&a_lock);unsigned char devAddr = 0;if(hxd019d_dbg_en == 1)printk("hxd019d: ------%s(%d)-------\n", __func__, __LINE__);devAddr = chipSlaveAddr << 1;iic_i2copen_hxd019();udelay(DELAY_TIME_HXD);iic_i2cstart_hxd019();udelay(DELAY_TIME_HXD);iic_I2CWriteData_hxd019(devAddr);udelay(DELAY_TIME_HXD);iic_I2CWriteData_hxd019(I2cRegAddr);udelay(DELAY_TIME_HXD);//iic_i2cstart();//udelay(DELAY_TIME);//iic_I2CWriteData((devAddr | 0x1));//udelay(DELAY_TIME);//iic_I2CReadData(&bValue);iic_I2CWriteData_hxd019(data);udelay(DELAY_TIME_HXD);iic_i2cstop_hxd019();udelay(DELAY_TIME_HXD);iic_i2cclose_hxd019();udelay(DELAY_TIME_HXD);//spin_unlock(&a_lock);return 0;   }

Linux驱动实现GPIO模拟I2C读写操作相关推荐

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

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

  2. Linux下使用GPIO模拟I2C IIC驱动(PCF8563)

    此代码实现LINUX下,创建/dev/algortc设备,但是没有实现rtc标准的读写接口,仅仅实现了IIC时序模拟,编译出.ko文件之后,使用insmod命令挂载,代码里面的线程就会循环读写PCF8 ...

  3. linux下IO口模拟I2C的一些总结

    2019独角兽企业重金招聘Python工程师标准>>> 以前一直在用I2C接口,因为总是有线程的例子就一直没有去深入的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序. ...

  4. stm32_GPIO模拟I2c读写EEPROM

    /* 下面给出STM32通过GPIO模拟I2C读写EEPROM程序 */ #define SCL_H GPIOB->BSRR = GPIO_Pin_6 #define SLC_L GPOIB-& ...

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

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

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

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

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

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

  8. i2c 驱动五:gpio模拟i2c

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

  9. GPIO模拟I2C通信协议(二)

    GPIO模拟2C读写E2PROM 1 E2PROM简介 2 AT24C28的读写逻辑 2.1 单字节写入 (BYTE WRITE) 2.2 页写入 (PAGE WRITE) 2.3 读取当前地址 (C ...

  10. GPIO模拟I2C程序实现

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

最新文章

  1. 《妙解Hibernate 3.X》读书笔记一-Hibernate概述及环境搭建
  2. python绘制条形图-python 中条形图绘制
  3. python -- IO多路复用
  4. OpenCV 对一张图片进行缩放
  5. mac 键盘按键符号讲解
  6. Oracle 分页查询语句SQL
  7. S3C2440启动代码分析
  8. 计算机英语讲课笔记04
  9. 4.4 核对矩阵的维数
  10. APP自动化测试系列之Appium介绍及运行原理
  11. 推荐系统评测指标—精准率(Precision)、召回率(Recall)、F值(F-Measure)
  12. chrome18-使用network waterfall分析页面载入性能
  13. yaourt 之 Curl 错误
  14. win10配置Sublime Text 3作为latex的编辑器
  15. ORACLE 正负数分开排序 SQL
  16. php 读取并显示doc,PHP读取doc,docx,xls,pdf,txt内容
  17. unity 构建迷宫_教程:使用GameDraw在Unity中构建迷宫游戏关卡
  18. kali linux 最新下载,kali新版本下载,Kali Linux 2019.4 开放下载
  19. 如何使用智能手机控制PlayStation 4
  20. 计算机的发展经历起源与未来,计算机起源与发展历程.ppt

热门文章

  1. NOIP2016愤怒的小鸟 题解报告 【状压DP】
  2. Windows环境下32位汇编语言程序设计笔记-基础篇
  3. 【JavsScript】作用域链
  4. PHP文件系统-文件的读写操作
  5. python的类与继承
  6. 问答知识图谱还缺少什么信息?
  7. 【论文解读】知识图谱最新研究综述
  8. 【QA】揭开知识库问答KB-QA的面纱1·简介篇
  9. 一次性送出25本北大出版社AI类当当最畅销的25本书!包括~机器学习、深度学习实战、数学基础等...
  10. Linux基础—2.Linux中的文件基本命令以及四大远程连接工具XShell、FileZilla、Notepad++、Xmanager4的使用