驱动专题:第四章 IIC驱动

本章节分为5部分

IIC基本概念和基本时序

IIC通信协议

IIC裸驱

OS下的IIC驱动

Linux下的IIC驱动


IIC基本概念和基本时序

1、I2C总线具有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL

  

2、IIC总线上可以挂很多设备:多个主设备,多个从设备(外围 设备)。上图中主设备是两个单片机,剩下的都是从设备。 

3、多主机会产生总线裁决问题。当多个主机同时想占用总线时,企图启动总线传输数据,就叫做总线竞争。I2C通过总线仲裁,以决定哪台主机控制总线

4、上拉电阻一般在4.7k~10k之间

      

5、每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传输可以是由主机发送数据到其它器件,这时主机即为发送器,总线上收数据的器件则为接收器。

6、I2C总线的数据传送:

    (1)、数据位的有效性规定:

          

     (2)、起始与终止信号:SCL为高期间,

            SDA : 由高到低,起始信号

             SDA:由低到高,终止信号

           

7、起始信号和终止信号都是由主机发送的。在起始信号产生之后,总线就处于被占用的状态,在终止信号产生之后,总线就处于空闲状态。

8、连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。

9、每当发送器传输完一个字节的数据之后,发送端会等待一定的时间,等接收方的应答信号。接收端通过拉低SDA数据线,给发送端发送一个应答信号,以提醒发送端我这边已经接受完成,数据可以继续传输,接下来,发送端就可以继续发送数据了。 

10、数据传送格式:主机发送给从机

       

11、I2C模拟方式 的特殊情况:

     

12、总线寻址:

      (1)、主机向从机发送8位数据,这8位数据是在起始信号之后发送的第一个字节,后面的字节都是数据,不再是寻址,除非又重新来一个起始信号。

        

     (2)、主机给从机发送第一个字节(总线寻址那个字节),若是读命令,则从机接收到该命令之后,主动往主机发送数据。

     (3)、主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器

    (4)、从机地址的确定:第0位是读写位。(如对于24C02这块存储器,它若作为从机,那么它的地址中7~4位是固定的,更改不了,第3~1位是可以更改的,每一位根据硬件的管教连接来确定,连接高电平那就是1,低电平就是0)         

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

14、每次数据传送总是由主机产生的终止信号来结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

15、在总线的一次数据传输中,可以有一下几种组合方式:

      (1)、主机向从机发送数据,数据传送方向在整个传递过程中不变:

        

       (2)、主机在第一个字节后,立即从从机读数据(传输方向不变):

               

        (3)、在传送过程中,当需要改变传递方向时,起始信号和从机地址都被重复一次产生一次,但两次读/写方向位正好相反

                

16、时序:

      

注:主机做的都是编程控制,从机做的都是自主控制,也可以说是硬件控制,如主机给应答信号是编程控制,但是从机给应答信号是硬件控制,我们只需要检查在SDA为高期间,SCL保持低电平一些时间,即可判定从机给了主机应答信号。

参考:

https://blog.csdn.net/shaguahaha/article/details/70766665

IIC详解,包括原理、过程,最后一步步教你实现IIC

IIC通信协议

1.开始和停止条件

SCL时钟电平为高

SDA数据线由高 -> 低 为总线开始条件

SDA数据线由低 -> 高 为总线结束条件

(注意:开始之后将SCL变为低电平,防止误操作SDA使其通信停止,见源代码)

时序图

源代码程序:

2.数据位传输

SCL时钟电平为低, 可以改换SDA数据线的电平,在SCL上升沿的过程将SDA数据发送出去。

(切记:请先将SCL变为低电平,再改变SDA电平状态。主要用于I2C读写Byte函数,这两个函数网上很多人写的不规范,引用需注意,在下面我会举例说明)

时序图

发送一位“高”数据流程:

SCL_LOW时钟低 ->  SDA_HIGH数据 -> SCL_HIGH时钟高

3.应答位信息

I2C 是以字节(8位)的方式进行传输,总线上每传输完1字节之后会有一个应答信号,主器件(主机)需要产生对应的一个额外时钟。

应答位产生及接收

1.在(主机)写数据的时候是从机应答(给主机),主机检测;

2.在(主机)读数据的时候是主机应答(给从机),从机检测;

(这里可以借助I2C读写函数一起理解)

1.时序图(主机写,从机应答,主机读取应答)



2.时序图(主机读,主机产生应答)

4.I2C写一字节

这里说的I2C写,是主机往从机接入1Byte的数据;

“写”要求按照上面的“数据为传输”来操作:在SCL时钟为低电平时准备好,待SCL为高电平时发送出去。

完一字节(8位)之后,读取从机的应答位:

若为0,表示从机应答,可以继续下一步操作;

若为1,表示从机非应答,不能进行下一步操作。

注意:

I2C写一字节不是EEPROM写一字节(需要区分开来)。

“简洁版”没有对应答信号做出检测判断,需要检测应答信号,可参考“综合版”

写一字节时序(前面8位数据 + 最后1为应答):

源代码程序:

5.I2C读一字节

I2C的读一字节函数,其实和“写一字节”类似,只是数据传输方向相反,应答的方向也是相反。

完一字节(8位)之后,由主机产生应答(或非应答)位:

若产生应答,表示可以继续读下一字节操作(从设备地址指向下一字节);

若产生非应答,表示不可以继续读下一字节操作;

网上I2C读数据程序和“写数据”类似,存在很多不标准的版本,参考时请注意。

读一字节时序(主机读取前面8位数据 + 主机产生1为非应答<连续读,主机产生应答位>):

读字节源代码程序:

参考博客:

https://blog.csdn.net/ybhuangfugui/article/details/52151835

STM32F10x_模拟I2C读写EEPROM


IIc裸驱

以EEPROM为例进行讲解

EEPROM的种类比较多,大多数都遵循I2C协议通信,我们这里就以典型的AT24Cxx为例来讲述通过I2C通信读写AT24Cxx芯片。

EEPROM读(或写)一字节数据需要I2C多次通信过程,下面将讲述几个重要的内容:

1.设备(从机、器件)地址

I2C的开始信号之后的第一步就是发送设备物理地址, AT24Cxx的物理地址的格式如上面:

前面四位固定为:1010

第567位对应A2 A1 A0(有些器件未使用)

第8位是读/写位。

一个设备一般是接地,这就是为什么我们看到A0这个宏定义的来由。

2.数据地址长度

有些芯片数据地址只有8位(如:AT24C01、AT24C02),那么它只发送一字节地址即可;

有些芯片有16位地址,它需要发送两字节地址(看下面读写函数)。

3.EEPROM写一字节数据

EEPROM写数据一般包含下面五步骤(见下面源代码)。这里的写数据,相当于手册中是随机写(任意地址,写一字节数据)。

(未检测应答,需要可以看我提供的另一个源代码程序)

注意两个地方:1.设备地址更加需要看你看引脚的情况;

2.数据地址长度根据芯片不同而不同。

4.EEPROM读一字节数据

EEPROM读数据和写数据相比,要多两个步骤(见下面源代码)。由于要先确定读的地址,所以要先发送地址,使其EEPROM指向对应的地址。(当然,如果当前地址就是需要读取的地址,也可以省略前面发送地址的步骤)。

(在手册中有这么一个步骤“Dummy Write”,有些人把它翻译为“伪操作”,可能很多人不明白它的意思,其实就是确定地址,先要发送地址的意思)

具体请看源代码:(未检测应答,需要可以看我提供的另一个源代码程序)

    跟多关于EEPROM的操作(如:页写、多字节读写等),相对来说复杂一点,当你理解单字节读写操作之后,再去理解就容易的多了。具体内容可以下载我提供的实例参考学习。

接下来详细讲述IIC裸驱的实现

Ⅰ、写在前面

实例实验效果:

1、多字节读写:任意地址(66), 写入任意长度(129)、读取并打印出来

2、单字节读写:任意地址(0),写入1字节数据、 读取并打印出来

实验说明:

1.多字节读写

实验为什么是从66地址写?为什么是写入129字节?

答案:验证对EEPROM多字节“非标准地址、长度”读写的准确性。

我是使用AT24C128芯片,页大小是64字节,我从66地址,就是验证非标准地址(如:0、64、128等)开始读写;写入长度129字节也是验证非标准长度(如:64、128、256等)的读写。

2.单字节读写

我这样实验的目的,相信大家都能理解。验证每一次写入字节数据和读出的数据是都一致。

Ⅱ、硬件I2C配置

硬件I2C的配置其实很简单,RCC时钟、GPIO、I2C配置等。笔者以F1标准外设库(同时也建议初学者使用官方的标准外设库)为基础建立的工程,主要以库的方式来讲述(若您的F1芯片与提供工程不一样,可微信回复“修改型号”)。

1.RCC时钟源

该函数位于bsp.c文件下面;

RCC是很多初学者,甚至已经工作的朋友容易遗漏的地方,有很多朋友觉得它使用的外设不正常,很大部分是没有配置RCC导致的。

重点注意:

A.外设RCC时钟的配置要在其外设初始化的前面;

B.匹配对应时钟。

比如:RCC_APB2外设不要配置在RCC_APB1时钟里面

【如:RCC_APB1PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);这样能编译通过,但这是错误的代码】

2. I2C引脚配置

 

该函数位于i2c_ee.c文件下面;

1.使用硬件I2C:GPIO_Mode_AF_OD复用开漏模式

2.由于使用硬件I2C,不像使用模拟I2C使用IO操作,所以这里引脚定义的比较“死”GPIO_Pin_6 | GPIO_Pin_7。

如果你使用I2C2或者引脚映射,这里的引脚也要跟着改变。

3. I2C配置

该函数位于i2c_ee.c文件下面;

这个函数才是本文的重点

1.I2C模式:I2C_Mode= I2C_Mode_I2C;

硬件有多种模式:

I2C_Mode_I2C: I2C模式

I2C_Mode_SMBusDevice: SMBus设备(丛机)模式

I2C_Mode_SMBusHost: 主机模式

2.I2C占空比:I2C_DutyCycle= I2C_DutyCycle_2;

这个参数在快速I2C模式下有效,也就是速度大于100KHz。

I2C_DutyCycle_2:2比1占空比

I2C_DutyCycle_16_9:16比9占空比

感兴趣的朋友可以把时钟配置高于100KHz(如:400KHz),用示波器测一下SCL引脚,可以看得出来占空比不一样。

3.I2C设备地址:I2C_OwnAddress1= EEPROM_DEV_ADDR;

这个参数是第一个设备(从机)的地址,EEPROM_DEV_ADDR是我们自己宏定义的设备地址。

4.I2C应答:I2C_Ack= I2C_Ack_Enable;

这个参数的含义请结合上一篇文章“I2C协议”来理解。

5.地址位数:I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;

这个参数就是设备地址位数,需要和后面函数“I2C_Send7bitAddress”一致。

6.I2C速度:I2C_ClockSpeed= I2C_SPEED;

这个参数很好理解,I2C_SPEED是我们宏定义的值“100000”,也就是100KHz的意思。

Ⅳ、硬件I2C读写EEPROM配置

上一篇文章简单提及了一下EEPROM单字节的读写,提供了多字节读写实例,但没有具体描述多字节的具体操作。

下面将详细描述一下单字节读写多字节读写的操作。请下载“I2C EEPROM资料”和“实例工程”作为参考。

在对EEPROM(AT24Cxx)读写操作之前需要理解两个参数(可见源代码i2c_ee.h文件):

A.“数据字”地址长度:也就是存数据的地址有多少位。具体分类(见数据手册)如下:

 8位:AT24C01、AT24C02

16位: AT24C04、AT24C08、AT24C16、AT24C32、AT24C64、AT24C128、AT24C256、AT24C512

B.页长度:在进行连续写的时候,最长可写一页,写完这一页之后需要指定下一页地址才行,否则会在上一页循环写。具体分类(见数据手册)如下:

  8字节:AT24C01、AT24C02

 16字节:AT24C04、AT24C08、AT24C16

 32字节:AT24C32、AT24C64

 64字节:AT24C128、AT24C256

128字节: AT24C512

1. 单字节写

时序图:

截图来自“AT24C128C数据手册”,单字节写主要分5个步骤

1.开始

2.设备地址/写

3.数据地址

4.写一字节数据

5.停止

源程序:

在操作硬件I2之前需要检测I2C是否处于“忙”状态。数据地址根据长度不同而写入的不同。

2. 单字节读(随机)

时序图:

截图来自“AT24C128C数据手册”,单字节读(也是随机读)主要分7个步骤

1.开始

2.设备地址/写

3.数据地址

4.重新开始

5.设备地址/读

6.读一字节数据

7.停止

源程序:

这里就提醒一点,单字节读和多字节读的应答位,由于不连续读,这里产生非应答

3. 页写

时序图:

截图来自“AT24C128C数据手册”,页写单字节写的区别在于“连续写”。

注意:这里页写的意思是在指向地址的页写数据,也就是EEPROM内部“地址指针”指向的地址所在页。每次写之前我们都要将“地址指针”指向一个地址(见下面源程序),写的过程中,一旦写到最后一个字节,将会回到该页首地址继续写下去,因此,写完该页,我们需要重新将“地址指针”指向下一页首地址。

【芯片页的大小根据芯片不同而不同,见本章开头描述】

源程序:

写最后一字节独立出来是有原因的:防止HardFault_Handler。、

4. 多字节写

源程序:

“多字节写”是基于“页写”的基础上写的,从上面页写的描述(写到该页最后一字节会回到该页首地址)可以知道多字节写是要考虑很多情况的,否则会破坏其他数据。

上面源程序截取了简单的一部分:开始写的地址刚好位于该页首地址这种情况。在页首地址开始写数据情况下,要判断需要写的数据的大小是否有多页。

上面这种情况是比较简单的一种,还有其他情况,我不在这里讲述,希望初学的你多去理解一下,这也是参考ST官方的思路,而且有利于你们编程的思想

5. 多字节读

时序图:

截图来自“AT24C128C数据手册”,多字节读需要注意应答

多字节读到最后一位数据之前,必须产生应答位,而最后一位产生非应答位。请结合下面源程序理解。

源程序:

单字节读比:前面第1步到第5步都是一样的,重点请看第6步,这里产生的应答需要注意。

参考博客:

https://blog.csdn.net/ybhuangfugui/article/details/52175621

STM32F10x_硬件I2C读写EEPROM(标准外设库版本)

https://blog.csdn.net/ybhuangfugui/article/details/52151835

STM32F10x_模拟I2C读写EEPROM

OS下的IIC驱动

Linux下的IIC驱动

Linux 的I2C 核心、总线与设备驱动

IIC总线特点
1、结构简单,只需两根信号线,SDA,SCL
2、简化对硬件的空间占用
3、广泛应用于EEPROM,实时钟,小型LCD设备


Linux下的IIC驱动体系结构~3部分
IIC核心,IIC总线驱动,IIC设备驱动,3部分相互协作,形成通用,适应性强的IIC框架
第一节对 Linux I2C 体系结构进行分析,讲解 3 个组成部分各自的功能及相互联系
第二节对 Linux I2C 核心进行分析,讲解 I2C-core.c文件的功能和主要函数的实现。
第三第四节分别详细介绍 I2C 总线驱动和 I2C 设备驱动的编写方法,给出可供参考的设计模板。
第五第六节以三节和四节给出的设计模板为基础,讲解S3C2410 ARM
处理器I2C总线驱动及挂接在其上的SAA7113H视频模拟/数字转换芯片设备驱动的编写方法。


第一节对 Linux I2C 体系结构进行分析,讲解 3 个组成部分各自的功能及相互联系
Linux 的I2C 体系结构的3 个组成部分


(1)I2C 核心。
I2C 核心提供了I2C 总线驱动和设备驱动的注册、注销方法,I2C 通信方法
上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
(2)I2C 总线驱动。
I2C 总线驱动是对I2C 硬件体系结构中适配器端的实现,
适配器可由CPU 控制,甚至可以直接集成在CPU 内部。
I2C 总线驱动主要包含了I2C 适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm 和控制I2C 适配器产生通信信号的函数。
经由 I2C 总线驱动的代码,我们可以控制I2C 适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK 等。
(3)I2C 设备驱动。
I2C 设备驱动是对I2C 硬件体系结构中设备端的实现,
设备一般挂接在受CPU 控制的I2C 适配器上,通过I2C 适配器与CPU 交换数据。
I2C 设备驱动主要包含了数据结构i2c_driver 和i2c_client,我们需要根据具体设备实现其中的成员函数。

Linux IIC源文件目录组成
(1)i2c-core.c。
这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。
通用的一些接口,跟具体硬件操作无关
(2)i2c-dev.c。
实现了 I2C 适配器设备文件的功能,每一个I2C 适配器都被分配一个设备。通过适配器访问设备时的
主设备号都为89,次设备号为0~255。应用程序通过“i2c-%d”(i2c-0, i2c-1,…, i2c-10,…)文件名并使用
文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。
i2c-dev.c 并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,应用层可
以借用这些接口访问挂接在适配器上的I2C 设备的存储空间或寄存器,并控制I2C 设备的工作方式。
主要功能:通用的i2c设备驱动
1、实现file_operations并注册file_operations
2、实现并注册适配器端的i2c_driver

(3)chips 文件夹。
这个目录中包含了一些特定的 I2C 设备驱动,如Dallas 公司的DS1337 实时钟芯片、EPSON 公司的
RTC8564 实时钟芯片和I2C 接口的EEPROM 驱动等。
1、实现并注册设备端的i2c_driver
(4)busses 文件夹。
这个文件中包含了一些 I2C 总线的驱动,如S3C2410 的I2C 控制器驱动为i2c-s3c2410.c。
主要功能:
1、注册platform_driver
2、实现platform_driver的成员函数,probe,remove,suspend_late,resume等
(5)algos 文件夹。
实现了一些 I2C 总线适配器的algorithm。
此外,内核中的 i2c.h 这个头文件对i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 这4 个数据结
构进行了定义。理解这4 个结构体的作用十分关键
关键数据结构之间的关系
i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 这4 个数据结构的作用及其盘根错节的关系。
这里有张比较详细的图
第二节对 Linux I2C 核心进行分析,讲解 I2C-core.c文件的功能和主要函数的实现。
1、I2C核心提供的几组重要函数
增加删除i2c_adapter,增加删除i2c_driver,client依附和脱离
I2C传输、发送和接收函数
I2C控制命令分配函数
第三节详细介绍 I2C 总线驱动的编写方法,给出可供参考的设计模板。
I2c-s3c2410.c里的代码即是i2c的总线驱动
1、I2C适配器驱动加载和卸载函数完成的主要工作
在buses里面的probe函数里添加i2c_adapter
2、I2C总线通信方法
主要要实现两个函数,master_xfer和functionality,在s3c24xx_i2c_probe里被赋值给s3c24xx_i2c类型的结构体,最终会被赋值给pdev->dev->driver_data
最核心的就是实现platform_driver的成员函数,probe,remove,suspend_late,resume等,最后通过platform_driver_register注册上去。
第四节详细介绍 I2C 设备驱动的编写方法,给出可供参考的设计模板。
https://www.linuxidc.com/Linux/2017-03/142198.htm
Linux i2c子系统(一) _动手写一个i2c设备驱动,这篇讲的比较到位,之后的驱动讲解可以重点关注下这个网站上的东西,还不错。
https://blog.csdn.net/luckywang1103/article/details/16834519
i2c驱动之i2c-dev驱动
主要解释了如何操作一个i2c设备驱动
主要关注i2c_driver和i2c_client数据结构
1、设备驱动的加载和卸载
2、i2c_add_driver会引发i2c_driver结构体中attach_adapter执行
3、自己写的i2c驱动需要实现i2c_driver的成员函数,主要是attach_adapter和detach_adapter
在attach_adapter里可以做很多事情,参见华清的书籍,这个接口是在i2c_add_driver里会回调,也就是在模块insmod时,在初始化里去回调
4、在i2c设备驱动模块初始化里的调用关系和需要做的事情
5、i2c-dev.c实际上是一个虚拟的i2c设备驱动,针对i2c-dev.c的具体分析(用户空间的编程方法)
如何使用i2c驱动
第一种方法
https://www.linuxidc.com/Linux/2017-03/142198.htm
Linux i2c子系统(一) _动手写一个i2c设备驱动
第二种方法
https://www.linuxidc.com/Linux/2017-03/142199.htm
Linux i2c子系统(二) _通过i2c-dev.c访问设备的方法
第五节总线驱动编写实例
1、总线驱动需要完成的工作
适配器驱动的加载和卸载函数
定义私有信息结构体
I2c总线通信方法,除了必备的准备工作以外,还需要中断处理函数的参与
第六节SAA7113H视频AD芯片的I2C设备驱动实例
1、设备驱动的加载和卸载函数
2、i2c_driver的成员函数
https://blog.csdn.net/li_wen01/article/details/51658916
linux IIC子系统分析(七)——实例分析通过i2c-dev操作I2C设备
这边有个系列写IIC的,总结完毕后可以看看。

参考:

华清那本书里的东西也可以参考下《linux设备驱动开发详解》
https://blog.csdn.net/lizuobin2/article/details/51694574?locationNum=11&fps=1
i2c驱动程序全面分析,从adapter驱动程序到设备驱动程序
https://blog.csdn.net/lizuobin2/article/details/51713637
I2C协议->裸机程序->adapter驱动程序分析
https://blog.csdn.net/lizuobin2/article/details/51694574?locationNum=11&fps=1
i2c驱动程序全面分析,从adapter驱动程序到设备驱动程序
http://www.cnblogs.com/lcw/p/3297889.html
【驱动】linux下I2C驱动架构全面分析


猜你喜欢

转载自blog.csdn.net/chichi123137/article/details/80754262
今日推荐