本文的程序是主控室STM32F207,读取的芯片是PCF8563。各种宏定义和文件会在末尾说明。
IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司在八十年代初设计出来的一种简单、双向、二线制、同步串行总线,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。这种方式简化了信号传输总线接口。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
SDA(数据线)和SCL(时钟线)都是集电极开路或漏极开路结构的,只能输出“0”,不能输出“1”,都必须接上拉电阻。
当需要输入数据时,将一个读数据用的输入端接在输出端。
SCL线的时钟同步:SCL由于具有线“与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。当所有的节点都发送高电平时,总线才能表现为高电平。正是由于线“与”逻辑功能的原理,当多个节点同时发送时钟信号时,在总线上表现的是统一的时钟信号。
♣. SDA线的仲裁:SDA线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。
IIC的时序图
因为 只有两条线,从时序图上 看 也是 很简洁 的。
下面由GIF动画展示启动信号的过程
动画中红色的阴影区域,表示 IIC 启动信号
/*******************************************************************************
** Function name: I2C_Start
** Descriptions: 开始发送一个I2C起始信号
** Input parameters: None
** output parameters: None
** Created Date:
*******************************************************************************/
void I2C_Start(void)
{
//IO输出
SDA_OUT();
SCL_OUT();
I2C_DELAY();
//IO置高
SDA_SET();
SCL_SET();
//延时
I2C_DELAY();
//为低
SDA_CLR();
I2C_DELAY();
I2C_DELAY();
SCL_CLR();
}
理解了开始信号,再来看 停止信号,分分秒秒 就会的事情,前提:先理解 开始信号后,再来看 停止信号。
/*******************************************************************************
** Function name: I2C_Stop
** Descriptions: 发送一个I2C总线结束信号
** Input parameters: None
** output parameters: None
** Created Date:
*******************************************************************************/
void I2C_Stop(void)
{
//IO输出
SDA_OUT();
SCL_OUT();
//IO置0
SDA_CLR();
SCL_CLR();
I2C_DELAY();
SCL_SET();
//延时
I2C_DELAY();
I2C_DELAY();
I2C_DELAY();
//SDA置1
SDA_SET();
I2C_DELAY();
I2C_DELAY();
}
学习了 启动 和停止 信号后,就可以开始学习 写入一个字节 命令了
该命令 过程是,
文字描述还是比较 抽象 的,看一下 动画
利用 C51 编写代码,如下
*******************************************************************************
** Function name: I2C_Send_byte
** Descriptions: 主机从I2C总线发送一个字节
** Input parameters: data
** output parameters: 0-false 1_ture
** Created Date:
*******************************************************************************/
uint8 I2C_Send_byte(uint8 data)
{
uint8 k;
//发送8bit数据
for(k=0;k<8;k++){
I2C_DELAY();
if(data&0x80){
SDA_SET();
}
else{
SDA_CLR();
}
data=data<<1;
I2C_DELAY();
SCL_SET();
I2C_DELAY();
I2C_DELAY();
SCL_CLR();
}
//延时读取ACK响应
I2C_DELAY();
SDA_SET();
//置为输入线
SDA_IN();
I2C_DELAY();
SCL_SET();
I2C_DELAY(); //这里出现了问题,延时变的无限大
//读数据
k=SDA_READ();
I2C_DELAY();
SCL_CLR();
I2C_DELAY();
SDA_OUT();
if(k){ ////NACK响应
return 0;
}
return 1;
}
读取字节 的 资料 不多,和写一个字节有区别,不过 如果理解怎样写一个字节后,再理解 读字节 难度不大。
/*******************************************************************************
** Function name: I2C_Receive_byte
** Descriptions: 主机从I2C总线接收一个字节
** Input parameters: None
** output parameters: data
** Created Date:
*******************************************************************************/
uint8 I2C_Receive_byte(void)
{
uint32 k,data;
//接收8bit数据
//置为输入线
SDA_IN();
data=0;
for(k=0;k<8;k++){
I2C_DELAY();
SCL_SET();
I2C_DELAY();
//读数据
data=data|SDA_READ();
data=data<<1;
I2C_DELAY();
SCL_CLR();
I2C_DELAY();
}
data=data>>1; //往回移动1次
//返回ACK响应
//置为输出线
SDA_OUT();
SDA_CLR(); //输出0-ACK
I2C_DELAY();
SCL_SET();
I2C_DELAY();
I2C_DELAY();
SCL_CLR();
I2C_DELAY();
SDA_OUT();
//返回读取的数据
return (uint8)data;
}
MASTER 指主控制端,在一般系统中就是我们常说的单片机了;SLAVE是指具备I2C协议的专用IC
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
再来看一些常用器件的地址码
写一个字节方式,写完一个字节后就发停止符
写字符串方式,写完一个字节后 继续 写字节,直到发停止符
读字符串方式,读完一个字节后 继续 读字节,直到发停止符