简单的I2C协议理解
i2c协议(中文版)http://download.csdn.net/source/499286
一. 技术性能:
工作速率有100K和400K两种;
支持多机通讯;
支持多主控模块,但同一时刻只允许有一个主控;
由数据线SDA和时钟SCL构成的串行总线;
每个电路和模块都有唯一的地址;
每个器件可以使用独立电源
二. 基本工作原理:
以启动信号START来掌管总线,以停止信号STOP来释放总线;
每次通讯以START开始,以STOP结束;
启动信号START后紧接着发送一个地址字节,其中7位为被控器件的地址码,一位为读/写控制位R/W,R. /W位为0表示由主控向被控器件写数据,R/W为1表示由主控向被控器件读数据;
当被控器件检测到收到的地址与自己的地址相同时,在第9个时钟期间反馈应答信号;
每个数据字节在传送时都是高位(MSB)在前;
写通讯过程:
1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线;
2. 发送一个地址字节(包括7位地址码和一位R/W);
3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK);
4. 主控收到ACK后开始发送第一个数据字节;
5. 被控器收到数据字节后发送一个ACK表示继续传送数据,发送NACK表示传送数据结束;
6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;
读通讯过程:
1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线;
2. 发送一个地址字节(包括7位地址码和一位R/W);
3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK);
4. 主控收到ACK后释放数据总线,开始接收第一个数据字节;
5. 主控收到数据后发送ACK表示继续传送数据,发送NACK表示传送数据结束;
6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;
四. 总线信号时序分析
1. 总线空闲状态
SDA和SCL两条信号线都处于高电平,即总线上所有的器件都释放总线,两条信号线各自的上拉电阻把电平拉高;
2. 启动信号START
时钟信号SCL保持高电平,数据信号SDA的电平被拉低(即负跳变)。启动信号必须是跳变信号,而且在建立该信号前必修保证总线处于空闲状态;
3. 停止信号STOP
时钟信号SCL保持高电平,数据线被释放,使得SDA返回高电平(即正跳变),停止信号也必须是跳变信号。
4. 数据传送
SCL线呈现高电平期间,SDA线上的电平必须保持稳定,低电平表示0(此时的线电压为地电压),高电平表示1(此时的电压由元器件的VDD决定)。只有在SCL线为低电平期间,SDA上的电平允许变化。
5. 应答信号ACK
I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个ACK(把数据总线的电平拉低)来表示数据成功接收。
6. 无应答信号NACK
在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途:
a. 一般表示接收器未成功接收数据字节;
b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。
五. 寻址约定
地址的分配方法有两种:
1. 含CPU的智能器件,地址由软件初始化时定义,但不能与其它的器件有冲突;
2. 不含CPU的非智能器件,由厂家在器件内部固化,不可改变。
高7位为地址码,其分为两部分:
1. 高4位属于固定地址不可改变,由厂家固化的统一地址;
2. 低三位为引脚设定地址,可以由外部引脚来设定(并非所有器件都可以设定);
#define uchar unsigned char
uchar mid;
#define SCL 0x01 // IIC clock
#define SDA 0x02 // IIC data
#define true 1
#define false 0
/*===================================================================
// 函数功能: 模拟I2C接口初始化函数
// 形参: void
// 返回: void
// 编写: 2004/6/27
// 备注: 放于端口初始化之后调用
===================================================================*/
void Lib_I2C_Ini(void)
{
DDRC |= SCL;
DDRC |= SDA;
PORTC |= SCL;
PORTC |= SDA;
}
/*===================================================================
// 函数功能: 模拟I2C接口延时函数
// 形参: void
// 返回: void
// 编写: 2004/6/27
// 备注: 约延时7us,这样,I2C速度大约为100kHz
===================================================================*/
void Lib_I2C_Delay(void)
{
unsigned char tmp;
for(tmp = 0; tmp < 200; tmp++)
_NOP();
}
/*===================================================================
// 函数功能: 模拟I2C接口启动
// 形参: void
// 返回: void
// 编写: 2004/6/27
// 备注: 在SCL为高时间内变化SDA由高到低即为总线启动
===================================================================*/
void Lib_I2C_Start(void)
{
DDRC |= SDA;
PORTC |= SCL;
PORTC |= SDA;
Lib_I2C_Delay(); // START condition setup time
PORTC &= ~SDA;
Lib_I2C_Delay(); // START condition hold time
}
/*===================================================================
// 函数功能: 模拟I2C接口停止
// 形参: void
// 返回: void
// 编写: 2004/6/27
// 备注: 在SCL为高时间内变化SDA由低到高即为总线停止
===================================================================*/
void Lib_I2C_Stop(void)
{
DDRC |= SDA;
PORTC &= ~SDA;
PORTC &= ~SCL;
PORTC |= SCL;
Lib_I2C_Delay(); // STOP condition setup time
PORTC |= SDA;
Lib_I2C_Delay(); // STOP condition hold time
}
void Lib_I2C_CLK(void)
{
PORTC |= SCL;
Lib_I2C_Delay();
PORTC &= ~SCL;
}
/*===================================================================
// 函数功能: 模拟I2C接口接收一个ACK
// 形参: void
// 返回: unsigned char 收到的回复
// 编写: 2004/6/27
===================================================================*/
unsigned char Lib_I2C_ACK(void)
{
PORTC &= ~SCL;
DDRC &= ~SDA;
PORTC |= SCL; // Clock the ACK bit
Lib_I2C_Delay();
if((SDA & PINC) == SDA) // Check the ACK bit
{
PORTC &= ~SCL;
return true;
}
else
{
PORTC &= ~SCL;
return false;
}
}
/*===================================================================
// 函数功能: 模拟I2C接口送出一个数据
// 形参: data 要送出的数据
// 返回: void
// 编写: 2004/6/27
// 备注: SCL的下降沿读数
===================================================================*/
unsigned char Lib_I2C_Send(unsigned char data)
{
unsigned char i;
DDRC |= SDA;
PORTC &= ~SCL;
for(i = 0; i < 8; i++)
{
if((data & 0x80) == 0x80)
PORTC |= SDA;
else
PORTC &= ~SDA;
Lib_I2C_CLK();
data <<= 1;
}
// if(!Lib_I2C_ACK()) // no ack
// return false;
// else
// return true;
while(Lib_I2C_ACK());
return true;
}
/*===================================================================
// 函数功能: 模拟I2C接口接收一个字节数据
// 形参: void
// 返回: unsigned char 接收到的数据
// 编写: 2004/6/27
===================================================================*/
unsigned char Lib_I2C_Rcv(void)
{
unsigned char i;
unsigned char indata;
DDRC &= ~SDA;
for(i = 0; i < 8; i++)
{
indata <<= 1;
if(SDA & PINC) // receive logic 1
indata ++;
Lib_I2C_CLK();
}
Lib_I2C_CLK();
return indata;
}
//***********************************初始化M16硬件**********8
void port_init(void)
{
PORTA = 0x00;
DDRA = 0xff;
PORTB = 0x00;
DDRB = 0xff;
PORTC = 0x00; //m103 output only
DDRC = 0xff;
PORTD = 0x00;
DDRD = 0xff;
}
void Send_NByte(unsigned int Rom_add,unsigned int Adata)//********************************多字节数据的发送
{
Lib_I2C_Start();
Lib_I2C_Send(0xa0);//24C02的物理地址
Lib_I2C_Send(Rom_add);//数据存储地址
Lib_I2C_Send(Adata);//想要存储的数据
Lib_I2C_Stop();
delay_nms(10);
}
void Receive_NByte(unsigned int addr)//********************多字节接收
{
Lib_I2C_Start();
Lib_I2C_Send(0xa0);
Lib_I2C_Send(addr>>8);
Lib_I2C_Send(addr);
Lib_I2C_Start();
Lib_I2C_Send(0xa1);
mid=Lib_I2C_Rcv();
if (Lib_I2C_ACK())
delay_nus(10);
else
Lib_I2C_Stop();
}
void main(void)
{
unsigned char temp=0;
port_init();
uart_init();
Send_NByte(0x00aa,0x5a);
delay_nms(1000);
Receive_NByte(0x00aa);
while(1)
{
Receive_NByte(0x00aa);
PORTB=mid;
PORTB=0xff;
delay_nms(500);
}
}