void iic_start()//开始
{
IIC_SCL=1;
IIC_SDA=1;
delay_10us(1);
IIC_SDA=0;
delay_10us(1);
IIC_SCL=0;//变为低电平,繁忙状态,1为空闲状态
}
void iic_stop()//结束
{
IIC_SCL=1;
IIC_SDA=0;
delay_10us(1);
IIC_SDA=1;
delay_10us(1);//SCL在高电平,空闲状态
}
SCL:时钟信号
SDA:一根线,半双工的
上拉电阻
开漏模式下,开关接0闭合,开关接1断开(什么也没接,电平不稳定)
与传输不一样:传输时候SDA稳定
SCL为低电平时,发送器向数据线上发送一位数据,在此期间数据线上的数据允许发生变化,时钟线SCL为高电平期间接收器从数据线上读取一位数据,不允许发生变化了,必须保持稳定
主机发,地址是向谁发
一帧包含9位,最后一位是应答位
RA:接收应答,从机收到发0
蓝色S:开始
绿色S:发送地址与字节
R/W是读写位,1是读2是写
地址前四位固定,后三位可变换A0,A1,A2
主机读,地址是向谁读
发与收,去掉P
SA=1,停
功能实现:
sbit SCL=P2^1;
sbit SDA=P2^0;
void I2C_Start()
{
SDA=1;//确保SDA是1
SCL=1;//拉高时钟
SDA=0;//拉低SDA
SCL=0;//拉低时钟,形成时钟
}
void I2C_Stop()
{
SDA=0;//不知道SDA是啥,先拉低
SCL=1;//拉高时钟
SDA=1;//拉高SDA
SCL=0;
}
发送模块
因为开始的时候scl已经拉低了,发送字节只用放入数据在拉高读取就行,然后在拉低这是一个循环过程
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SDA=Byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
先高后低,不延时是1us,SCL拉高立马拉低不影响
接收模块:
因为主机在接收之前要释放SDA 所以将SDA又设置为默认的高电平了
unsigned char I2C_ReceiveByte()
{
unsigned char i,Byte=0x00;
SDA=1;
for(i=0;i<8;i++)
{
SCL=1;
if(SDA){Byte|=0x80;}
SCL=0;
}
}
#include <REGX52.H>
sbit SCL=P2^1;
sbit SDA=P2^0;
//我写一个东西要知道从机收没收到要读出SDA,
//我收一个数据我收到了要将SDA置1告诉从机
void I2C_Start()
{
SDA=1;//确保SDA是1
SCL=1;//拉高时钟
SDA=0;//拉低SDA
SCL=0;//拉低时钟,形成时钟
}
void I2C_Stop()
{
SDA=0;//不知道SDA是啥,先拉低
SCL=1;//拉高时钟
SDA=1;//拉高SDA
SCL=0;
}
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SDA=Byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
//SDA拉高是为了释放控制,当读取数据时,
//主机拉高,释放控制,从机往SDA上写入数据,
//主机拉高SCL读取数据
unsigned char I2C_ReceiveByte()
{
unsigned char Byte=0x00;
SDA=1;//控制权交给从机
for(i=0;i<8;i++)
{
SCL=1;
if(SDA){Byte|=0x80};//SDA 1接收,0不接收
SCL=0;
}
return Byte;
}
void I2C_SendAck(unsigned char Ackbit)
{
SDA=Ackbit;
SCL=1;
SCL=0;
}
unsigned char I2C_ReceiveAck()
{
unsigned char =0;
SDA=1;//接收需要释放总线,所以需要释放SDA,就是让从机有主导权
SCL=1;
AckBit=SDA;
SCL=0;
return AckBit;
}
普中写法
void iic_start()//开始
{
IIC_SCL=1;
IIC_SDA=1;
delay_10us(1);
IIC_SDA=0;
delay_10us(1);
IIC_SCL=0;//变为低电平,繁忙状态,1为空闲状态
}
void iic_stop()//结束
{
IIC_SCL=1;
IIC_SDA=0;
delay_10us(1);
IIC_SDA=1;
delay_10us(1);//SCL在高电平,空闲状态
}
void iic_ack()//应答
{
IIC_SCL=0; //低电平时,数据可以更改
IIC_SDA=0;
delay_10us(1);
IIC_SCL=1;//高电平读取
delay_10us(1);
IIC_SCL=0;
}
void iic_nack()//非应答
{
IIC_SCL=0; //低电平时,数据可以更改
IIC_SDA=1;
delay_10us(1);
IIC_SCL=1;//高电平读取
delay_10us(1);
IIC_SCL=0;
}
u8 iic_wait_ack()//读取从机响应,应答IIC_SDA=0,非应答应答IIC_SDA=1
{
u8 time_temp=0;
IIC_SCL=1;
delay_10us(1);
while(IIC_SDA)
{
time_temp++;
if(time_temp>100)
{
iic_stop();
return 1;//返回1非应答
}
}
IIC_SCL=0;
return 0;//返回0应答
}
void iic_write_byte(u8 dat)//写字节
{
u8 i=0;
IIC_SCL=0;//等于0数据可以改变
for(i=0;i<8;i++)
{
if((dat&0x80)>0)
IIC_SDA=1;
else
IIC_SDA=0;
dat<<=1;
IIC_SCL=1;
delay_10us(1);
IIC_SCL=0;
delay_10us(1);
}
}
u8 iic_read_byte(u8 ack)
{
u8 i=0;
u8 receive=0;
for(i=0;i<8;i++)
{
IIC_SCL=0;
delay_10us(1);
IIC_SCL=1;
delay_10us(1);
receive<<=1;//因为最高位
if(IIC_SDA==1)
{
receive++;
}
}
if(ack==0)iic_nack();
else iic_ack();
return receive;
}
#include "24c02.h"
void at24c02_write_one_byte(u8 addr,u8 dat)//地址与数据
{
iic_start();
iic_write_byte(0xa0);//地址1010 00000 前四位固定
iic_wait_ack();//等待应答
iic_write_byte(addr);
iic_wait_ack();//等待应答
iic_write_byte(dat);
iic_wait_ack();
iic_stop();
delay_ms(10);
}
u8 at24c02_read_one_byte(u8 addr)
{
u8 temp=0;
iic_start();//主机启动
iic_write_byte(0xa0);//从机地址
iic_wait_ack();//从机ack返回
iic_write_byte(addr);//写入读取地址
iic_wait_ack();
iic_start();
iic_write_byte(0xa1);
iic_wait_ack();
temp=iic_read_byte(0);
iic_stop();
delay_ms(10);
}