IIC学习
1 概述:
IIC是用两条双向的线,一条SDA(serial data line),一条SCL(serial clock).
SCL:上升沿将数据输入到每个EEPROM器件中,下降沿驱动EEPROM器件输出数据(边沿触发)
SDA:双向数据线,为OD门,与其它任意数量的OD与OC门成“线与”关系
2 输出级
每一个IIC总线器件内部的SDA、SCL引脚电路结构都是一样的,引脚的输出驱动与输入缓存连在一起。其中输出为漏极开路的场效应管,输入缓存为一只高输入阻抗的同向器,这种电路具有两个特点
1)由于SDA、SCL为漏极开路结构,因此它们必须接有上拉电阻,阻值大小为1K8(1800o)、4K7(4700o)、10K,但1K8时性能最好;当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线与关系。
2)引脚在输出信号的同时还将引脚上的电平进行检测,检测是否与刚才输出一致,为“时钟同步”和“总线仲裁”提供了硬件条件
3 主设备与从设备
系统中的所有外围器件都具有一个7位的“从器件专用地址码”,其中高4位为器件类型,由生产厂家制定,低3位为器件引脚定义地址,由使用者定义。主控器件通过地址码建立多机通信的机制,因此IIC总线省去了外围器件的片选线,这样无论总线上挂接多少个器件,其系统仍然为简约的二线结构。终端挂载在总线上,有主端和从端之分,主端必须是带有CPU逻辑模块,在同一总线上同一时刻使能有一个主端,可以有多个从端,从端的数量受地址空间和总线的最大电容400pF的限制。
主端主要用来驱动SCL line;
从设备对主设备产生响应;
二者都可以传输数据,但是从设备不能发起传输,且传输是受到主设备控制的。
4 速率
普通模式:100kHz
快速模式:400kHz
高速模式:3.4MHz
5协议
5.1 空闲状态
IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个期间的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
5.2 起始信号与停止信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号
5.3 ACK
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
如下图逻辑分析仪的采样结果:释放总线后,如果没有应答信号,SDA应该一直维持在高电平,但是如图中蓝色虚线部分所示,它被拉低为低电平,证明收到了应答信号。
总结:1)接收器在SCL的上升沿到来之前的低电平期间拉低SDA;
2)应答信号一直保持到SCL的下降沿结束;
5.4 数据的有效性
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
个人理解:虽然只要求在高电平期间保持稳定,但是要有一个提前量,也就是数据在SCL的上升沿到来之前就准备好,因为数据在SCL的上升沿打入到器件(EEPROM)中的。
5.5 数据的传送:
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发.
6 工作过程
总线上的所有通信都是由主控器引发的。在一次通信中,主控器与被控器总是在扮演着两种不同的角色。
6.1 主设备向从设备发送数据
主设备发送起始位,这会通知总线上的所有设备传输开始了,接下来主机发送设备地址,与这一地址匹配的slave将继续这一传输过程,而其他slave将会忽略接下来的传输并等待下一次传输开始。主设备寻址到从设备后,发送它所要读取或写入的从设备的内部寄存器地址;之后,发送数据。数据发送完毕后,发送停止位。
写入过程如下:
1 发送起始位
2 发送从设备的地址和读/写选择位;释放总线,等到EEPROM拉低总线进行应答;如果EEPROM接收成功,则进行应答;若没有握手成功或者发送的数据错误时EEPROM不产生应答,此时要求重发或者终止
3 发送想要写入的内部寄存器地址,EEPROM对其发出应答
4 发送数据
5 发送停止位
6 EEPROM收到停止信号后,进入到一个内部的写入周期,大概需要10ms,此期间任何操作都不会被EEPROM响应:(因此以这种方式的两次写入之间要插入一个延时,否则会导致失败)
详细
说明:主控器通过发送地址码与对应的被控器建立了通信关系,而挂接在总线上的其他被控器虽然同时也收到了地址码,但因为与其自身的地址不相符合,因此提前退出与主控器的通信
6.2 主控器读取数据的过程
在从slave读取数据前,必须先要告诉它那个内部寄存器是你想要读取的,因此必须先对其进行写入(dummy write)
读取过程
1 发送起始位
2 发送slave地址 + write bit set
3 发送内部寄存器地址
4 重新发送起始位,即restart
5 重新发送slave地址 + write bit set
6 读取数据
主机接收器在接收到最后一个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,以允许主机发出P信号结束传输。
7 发送停止位
详细
6.3 页写
6.3.1 页写数据过程
不同的器件页写的字节数也不同,2402是按8字节/页写,2404-08-16是按16字节/页写。而2432-2464则是按32字节页写;
页写初始化和字节写相同,只是主器件不会在第一个数据后发送停止条件,而是EEPROM的ACK以后,接着发送(7个)、(15个)、(31个)数据。EEPROM在收到数据后每个都应答0,最后仍需要主器件发送停止条件。终止页写条件。
在接收或发送每个数据后,字地址的(低3位)、(低4位)、(低5位)内部自动加1,高位地址不变,维持当前页。当内部产生的字地址达到该页边界地址时,随后的数据将写入该页的页首,这是会将先前的字节覆盖掉,要注意。
6.3.2 读页数据过程
主器件接收到一个数据后,应答ACK。只要EEPROM接收到ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器的地址末尾,地址自动回转到0.仍可继续顺序读取数据。主器件不应答0,而发送停止条件,即可结束顺序读操作。
7 实例程序
void SCL_Init()
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
} void SDA_Out_Init()
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
} void SDA_In_Init()
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStructure);
} void IIC_Init() //初始化总线
{
SCL_OUT=;
SDA_OUT=;
delay_us();
} void IIC_Start() //开始发送 SCL为高,SDA由高变低
{
SDA_Out_Init();
SCL_OUT=;
SDA_OUT=; delay_us();
SDA_OUT=;
delay_us(); SCL_OUT=;
} void IIC_Stop() //停止 SCL为高,SDA由低变高
{
SDA_Out_Init();
SCL_OUT=;
SDA_OUT=;
SCL_OUT=;
delay_us(); SDA_OUT=;
delay_us();
} u8 IIC_Wait_Ack() //等待应答
{
u8 time=; SDA_OUT=;
delay_us();
SDA_In_Init();
SCL_OUT=;
delay_us();
while(SDA_IN) //#define SDA_IN PBin(11)
{
time++;
if(time>) //如果超过250还无应答,IIC停止
{
IIC_Stop();
return ;
}
}
SCL_OUT=;
return ;
} void IIC_Ack() //应答信号
{
SCL_OUT=;
SDA_Out_Init();
SDA_OUT=;
delay_us(); SCL_OUT=;
delay_us();
SCL_OUT=;
} void IIC_NAck() //非应答
{
SCL_OUT=;
SDA_Out_Init();
SDA_OUT=;
delay_us(); SCL_OUT=;
delay_us();
SCL_OUT=;
} void IIC_Write_Byte(u8 data) //写一个字节数据
{
int i,receive=;
SDA_Out_Init(); receive=data;
SCL_OUT=;
for(i=;i<;i++) //相当于SCL低电平期间准备好要写入的数据,SCL跳变为高电平进行发送
{
if(receive&0x80)
SDA_OUT=;
else
SDA_OUT=;
receive=receive<<;
delay_us(); SCL_OUT=;
delay_us();
SCL_OUT=;
delay_us();
}
} u8 IIC_Read_Byte(u8 ack) //读一个字节
{
int i,txdata=;
SDA_In_Init(); SCL_OUT=; //
delay_us();
for(i=;i<;i++)
{
SCL_OUT=; //高电平保持数据的稳定;来一个上升沿,数据就将要读取的一位放在数据线SDA上,所以接下来就得左移,才能读取下一位数据
delay_us();
txdata=txdata<<;
if(SDA_IN)
txdata++;
delay_us(); SCL_OUT=;
delay_us();
}
if(!ack)
IIC_NAck();
else
IIC_Ack();
return txdata;
} void write_addr(u8 address,u8 data)
{
IIC_Start();
IIC_Write_Byte(0xA0); //发送从器件芯片的地址,由于IIC可以挂接好多芯片,所以需要先找到该芯片
IIC_Ack(); IIC_Write_Byte(address);//发送芯片的字节地址,需要确定将数据发送到芯片的某个位置,芯片大小有2K
IIC_Ack(); IIC_Write_Byte(data); //将数据写入到芯片内
IIC_Ack(); IIC_Stop();
delay_ms(); //数据写完之后需等10ms时间才能再次发送起始信号,这个时间叫写周期
} u8 read_addr(u8 address)
{
u8 temp; IIC_Start();
IIC_Write_Byte(0xA0); //器件地址
IIC_Ack();
IIC_Write_Byte(address);//想要读取的器件中的地址0到255
IIC_Ack(); IIC_Start();
IIC_Write_Byte(0xA1); //1表示读取
IIC_Ack(); temp=IIC_Read_Byte();
IIC_Ack(); IIC_Stop(); return temp;
} void write_len(u16 address,u32 data,u8 len)
{
u8 i;
for(i=;i<len;i++)
write_addr(address+i,(data<<(*i)&0xff));
} u32 read_len(u16 address,u8 len)
{
u8 j;
u32 temp;
for(j=;j<len;j++)
{
temp=temp<<;
temp=temp+read_addr(address+len-j-);
}
return temp;
} /**************************************
将数据写入指定的地址里,num要写入数据个数
*************************************/
void write(u8 addr,u8 *buffer,u8 num)
{
while(num--)
{
write_addr(addr,*buffer); //含有了stop
addr++;
buffer++;
}
} /**************************************
在指定的地址读取数据
*******************************************/
u8 read(u8 addr,u8 *buffer,u8 num)
{
while(num)
{
*buffer++=read_addr(addr++);
num--;
}
return *buffer;
} void write_twobyte(u8 address)
{
u8 i;
u8 tx[]={,,};
IIC_Start();
IIC_Write_Byte(0xA0); //发送的是从器件芯片地址,由于IIC可以挂接好多芯片,所以需要先找到该芯片
IIC_Ack(); IIC_Write_Byte(address);//发送芯片的字节地址,需要确定将数据发送到芯片的某个位置,芯片大小2K
IIC_Ack(); for(i=;i<;i++)
{
IIC_Write_Byte(tx[i]); //将数据写入到芯片内
IIC_Ack();
} IIC_Stop();
delay_ms();
}
IIC学习的更多相关文章
- Linux3.5—IIC学习分析
I2C控制器的设备对象内核已经实现并关联到platform总线. I2C控制器的驱动对象内核已经实现. 看mach-tiny4412.h /plat-samsung/目录下 /drivers/i2c/ ...
- 基于51单片机IIC通信的PCF8591学习笔记
引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...
- 基于51单片机IIC通信的AT24C02学习笔记
引言 最近在学习几种串行通信协议,感觉收获很多,这篇文章是学习IIC总线协议的第一篇文章,以后还会再写一篇关于PCF8591 IIC通信的ADDA转换芯片的文章. 关于IIC总线 IIC 即Inter ...
- IIC协议学习笔记
"移植"的重要性:并非所有的电路都得自己设计,到了一定阶段,"移植"也是一种学习能力.--CrazyBingo 转眼间期末又到了,最近开始了所谓的期末总预习,比 ...
- IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试
IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...
- PX01关于手机屏IIC触摸调试学习笔记
相关文件下载: 上位机工具:http://www.xk-image.com/download/blog/0002_TP调试/LcdTools20210605.rar 调试案例:http://www.x ...
- 8051学习笔记——IIC与EEPROM实验
main.c #include <reg51.h> #include "iic.h" #define AT24C02 0xa0 //AT24C02 地址 sbit LS ...
- 51单片机学习笔记(郭天祥版)(9)——IIC、EEPROM
IIC是两根线,单总线,只有一根数据线,发送数据和读取收据都是一根线,像我们之前学的AD.DA都是许多线,许多线的话,这样做系统可以少浪费资源,少浪费控制IO口的资源,这种并行的处理速度快.所以线越多 ...
- IIC通讯协议(非原创,转载他人,用于学习)
I2C协议:1.空闲状态 2.开始信号 3.停止信号 4.应答信号 5.数据的有效性 6.数据传输 IIC详解 1.I2C总线具有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL 2.IIC总 ...
随机推荐
- Jenkins自动化构建(一)执行selenium+python脚本
Jenkins执行python写的selenium自动化脚本,通常会遇到,执行打不开浏览器,查看jenkins构建Console Output控制台输出信息,发现脚本是执行了的,但是出错了,打开浏览器 ...
- 数据加密之MD5加密
MD5是一个安全的散列算法,有两个特点:1.输入两个不同的明文(一段原始的数字信息)不会得到相同的输出值2.根据输出值,不能得到原始的明文,即过程不可逆所以要解密MD5没有现成的算法,只能用穷举法,把 ...
- uml的几种关系总结
UML类图几种关系的总结 在UML类图中,常见的有以下几种关系:泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregati ...
- OBV15 案例5,上M10拉高出货
- python爬虫-基础入门-爬取整个网站《3》
python爬虫-基础入门-爬取整个网站<3> 描述: 前两章粗略的讲述了python2.python3爬取整个网站,这章节简单的记录一下python2.python3的区别 python ...
- Ajax技术之XMLHttpRequest(二)【XMLHttpRequest常用方法和属性】
一.XMLHttpRequest中常用的方法: (1)open()方法:用于设置进行异步请求目标的URL.请求方法以及其他参数信息. 函数原型:open("method",&quo ...
- python3 TypeError: a bytes-like object is required, not 'str'
在学习<Python web开发学习实录>时, 例11-1: # !/usr/bin/env python # coding=utf-8 import socket sock = sock ...
- Cocos Creator学习目录
目录 安装和启动 文件结构 编辑器基础 基本概念 (场景树 节点 坐标 组件 ) Cocos Creator 脚本简介 Cocos Creator调试 节点 cc.Node 组件开发cc.Compon ...
- html5-了解元素的属性
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- CSS选择符-----元素选择符
通配选择符(*) 选定所有对象 通配选择符(Universal Selector) 通常不建议使用通配选择符,因为它会遍历并命中文档中所有的元素,出于性能考虑,需酌情使用 & ...