本文的原作者链接http://www.51hei.com/bbs/dpj-41478-1.html
概述
I2C总线是内部总线,用来连接系统内部的芯片,如MCU与存储器、按键、LCD等;采用MSB(先发送高位)
串行总线,只有两根双向信号线:时钟线SCL(标准I2C的SCL频率为100KHz)、数据线SDA。
I2C总线通过上拉电阻接正电源。总线空闲时,两根线为高电平,连接到总线上的任意器件输出低电平都能将总线的信号线变低,即各器件的SCL、SDA都是“线与”关系。
每个接到I2C总线上的器件都有唯一的地址。单主机与其他器件之间通信方向固定无需仲裁;而在多主机系统中,为避免几个主机启动总线时产生混乱,I2C需要总线仲裁。
时序图
起始信号:SCL线为高电平期间,SDA线由高电平向低电平变化。
void IIC_Start(void)
{
IIC_SDA_Write(1); //SDA引脚置1
IIC_SCL_Write(1); //SCL引脚置1
Delay1Us(5); //延时5us
IIC_Touch_SDA_Write(0); //SDA引脚置0
Delay1Us(5); //延时5us
IIC_Touch_SCL_Write(0); //SCL引脚置0
Delay1Us(5); //延时5us
}
停止信号:SCL线为高电平期间,SDA线由低电平向高电平变化。
void IIC_Stop(void)
{
IIC_SCL_Write(1); //SCL引脚置1
IIC_SDA_Write(0); //SDA引脚置0
Delay1Us(5); //延时5us
IIC_SDA_Write(1); //SDA引脚置1
Delay1Us(5); //延时5us
}
数据有效:SCL线为高电平期间,数据线上的数据必须保持稳定,只有在SCL线为低电平期间,数据线上的数据才允许变化。
写字节操作
/***********字节写*************/
char IIC_WriteByte(char data)
{
char i,ret;
for(i=0; i<8; i++)
{
if((data << i) & 0x80) //data循环左移8次与0x80相与
IIC_SDA_Write(1); //SDA引脚置1
else
IIC_SDA_Write(0); //SDA引脚置0
//数据线上的SDA只有SCL为低电平期间变化
Delay1Us(4);
IIC_SCL_Write(1); //SCL引脚置1
Delay1Us(5);
IIC_SCL_Write(0); //SCL引脚置0
Delay1Us(4);
}
ret = IIC_SDA_Read(); //返回SDA引脚上的值,便于后期辨别从机发送的ACK,NACK
Delay1Us(4);
IIC_SCL_Write(1);
Delay1Us(4);
IIC_SCL_Write(0);
Delay1Us(4);
return ret;
}
读字节操作
/***********字节读*************/
char IIC_ReadByte(void)
{
char i ,ret=0;
IIC_SDA_Write(1);
IIC_SCL_Write(0);
Delay1Us(4);
for(i=0; i<8; i++)
{
IIC_SCL_Write(0);
Delay1Us(4);
IIC_SCL_Write(1);
ret <<= 1; //先左移1位
ret += IIC_SDA_Read(); //后加SDA引脚的数值
Delay1Us(4);
}
IIC_SCL_Write(0);
Delay1Us(4);
return ret; //返回读取的字节
}
字节传送
传送的每一个字节后必须跟随一位应答位(即一帧共有9位)。
应答:ACK,低电平"0"
非应答:NACK,高电平"1"
/******************************************************************************************************/
2020.3.1补充 :只关注其原理而不去尝试敲代码实现,留下的只有空白。
EEPROM的IIC读写应用
EEPROM是在单片机开发中常用到的存储设备,其用到的读写通信方式就是IIC,这里针对其常用的读写方式Page Write & Random Read进行代码编写。
Page Write
/*****************************************************************
****函数变量介绍: *buf --写数据指针
addr --EEPROM地址
reg --指定EEPROM的内部地址
cnt --写数据的长度
******************************************************************/
void IIC_Tx(char *buf, char addr, char reg, char cnt)
{
char i;
IIC_Start();
//发送device address
if(IIC_WriteByte(addr<<1)==IIC_NACK)
{
}
Delay1Us(4);
//发送word address
if(IIC_WriteByte(reg)==IIC_NACK)
{
}
Delay1Us(4);
//发送写数据
for(i=0; cnt>0; i++,cnt--)
{
if(IIC_WriteByte(*(buf+i))==IIC_NACK);//发送完最后一个字节后,从机发送NACK信号
Delay1Us(4);
}
IIC_Stop();
}
Random Read
/*********************************************************
******变量说明 *buf --读数据指针
addr --EEPROM地址
reg --word address
cnt --要读数据的长度
*********************************************************/
void IIC_Rx(char *buf, char addr, char reg, char cnt)
{
IIC_Start();
//发送device address
if(IIC_WriteByte(addr<<1)==IIC_NACK)
{
}
Delay1Us(4);
//发送word address
if(IIC_WriteByte(reg)==IIC_NACK)
{
}
Delay1Us(4);
IIC_Start();
//发送device address
if(IIC_WriteByte(addr<<1|1)==IIC_NACK)
{
}
Delay1Us(4);
//读取数据
for(; cnt>0; buf++,cnt--)
{
*buf = IIC_ReadByte();
if(cnt==1)
IIC_SDA_Write(IIC_NACK);//读到最后一个字节,MCU发送NACK信号
else
IIC_SDA_Write(IIC_ACK);
Delay1Us(4);
}
IIC_Stop();
}
UART(串口通信)
起始位:"0"
数据位:有多种选择,常用的是8位(8bit=1byte)
奇偶校验位:奇校验或者偶校验
停止位:"1"
停止位:"1"