i2c通信,基于51与E2PROM(个人学习笔记)

i2c总线

I2C 属于同步通信, SCL 时钟线负责收发双方的时钟节拍, SDA 数据线负责传输数据。 I2C 的发送方和接收方都以 SCL 这个时钟节拍为基准进行数据的发送和接收。

SCL处于高电平时,SDA稳定,数据无效;SCL处于低电平时,SDA可变,数据有效

使用 i2c 的步骤

1.设置通信接口,并对接口进行初始化

2.起始信号、停止信号

2.(1)起始信号
2.(2)停止信号

3.应答信号

3.(1)写应答
3.(2)读应答

4.总线上写数据或收数据

4.(1)向总线写一字节的数据
4.(2)从总线读一字节的数据
4.(3)收到完成信号,结束通信,释放总线
4.(4)从总线读数据
(所有代码都基于51环境)

设备布置

设备布置

时序图

时序
时序

1.初始化

将SCL、SDA置高,总线处于空闲状态

void init()
{
    
    
	sda = 1;
	scl = 1;
}

2.起始信号、停止信号

2.(1)起始信号

I2C 通信的起始信号的定义是 SCL 为高电平期间, SDA 由高电平向低电平变化产生一个下降沿,表示起始信号。

所以,在SCL处于高电平、SDA高电平的状态下,拉低SDA, 产生高电平->低电平的下降沿,表示起始信号

void start()
{
    
    
	scl=1;
	sda=1;
	delay(2); //延时
	sda=0;
	delay(2);
	scl=0;  //释放总线
}
2.(2)停止信号

I2C 通信停止信号的定义是 SCL 为高电平期间, SDA 由低电平向高电平变化产生一个上升沿,表示结束信号。

所有,在SCL处于高电平、SDA低电平的状态下,拉高SDA,产生低电平->高电平的下降沿,表示起始信号

void stop()
{
    
    
	sda=0;
	scl=1;
	delay(2);
	sda=1;
	delay(2);
}

3.应答信号

应答信号用于表明I2C总线数据传输的结束。

3.(1)发送应答信号

SCL处于低电平下,拉高SDA,主机产生应答

void SendACK()
{
    
    
  scl=0;
  delay(2);
  sda = 1;
  scl = 1; 
  delay(2);
  scl = 0; //释放总线
  delay(2);
}
3.(2)等待应答信号

SCL高电平下,等待SDA拉高

void respons()
{
    
    
	unsigned char i;
	scl=1;
	while((sda==1)&&(i<250))  //等待一个while循环的时间\
                                接收到应答信号或者超过循\
                                环时间直接释放总线,完成传输
	{
    
    	                      
		i++;
	}
	scl=0;  //释放总线
	delay(2);
}

4.数据传输

I2C 没有固定波特率,但是有时序的要求,要求当 SCL 在低电平的时候, SDA 允许变化。

i2c传输数据为一帧一帧的传,一帧为一个字节

4.(1)向总线写一字节的数据
void write_byte(unsigned char dat)
{
    
    
	unsigned char i;

	for(i=0;i<8;i++)
	{
    
    
		scl=0;
		dat<<=1;
		sda=CY;
		scl=1;
		delay(2);
		scl=0;
		delay(2);
	}
	sda=1;
	delay(2);
}
4.(2)从总线读一字节的数据
unsigned char read_byte()
{
    
    
   unsigned char i,j,k;
   scl=0;
   delay(2);
   for(i=0;i<8;i++)
   {
    
    
   	scl=1;
   	delay(2);
   	j=sda;
   	k=(k<<=1)|j;
   	delay(2);
   	scl=0;
   	delay(2);
   }
   return k;
}
4.(3)向从机写数据

在通信的起始信号(Start)后,首先要发送一个从机的地址,这个地址一共有 7位,紧跟着的第 8 位是数据方向位(R/W),“ 0”表示接下来要发送数据(写),‘“ 1”表示接下来是请求数据(读)。第九位 ACK应答后才真正开始写数据。

void I2C_write(unsigned char device_add, unsigned char in_add, unsigned char dat)   //(设备地址,设备寄存器地址,数据)
//(51跟EEPROM通信,需要寄存器地址;根据需要修改)
{
    
    
	init();
	start();
	write_byte(device_add); //写设备地址,最后一位位决定读\写
	respons();
	write_byte(in_add); //写寄存器地址,决定在EEPROM的哪一块空间写数据
	respons();
	write_byte(dat); //写数据
	respons();
	stop();
}
4.(4)从总线读数据
 unsigned char I2C_read(unsigned char device_add, unsigned char in_add)//(设备地址,设备寄存器地址)
{
    
    
	unsigned char dat;
	init();
	start();
	write_byte(device_add);
	respons();
	write_byte(in_add);
	respons();

	//确定设备地址后,准备读数据

	start();
	write_byte(device_add+1); //初始地址device_add最后一位为0写数据,加1后变为读
	respons();
	dat = read_byte();
	SendACK(1);
	stop();
	
	return dat;
}

示例

EEPROM读取
代码:
i2c.h

#ifndef  __I2C_H__
#define  __I2C_H__

#include <reg52.h>

//不同设备根据需求修改总线
sbit sda = P2^0;
sbit scl = P2^1;


//函数初始定义
void delay(unsigned char x);
void nop_(unsigned char i);
void init();
void start();
void stop();
void SendACK(bit ack);
void respons();
void write_byte(unsigned char dat);
unsigned char read_byte();
void I2C_write(unsigned char device_add, unsigned char in_add, unsigned char dat);
unsigned char I2C_read(unsigned char device_add, unsigned char in_add);


//微延时函数
void delay(unsigned char x)
{
    
    
	while(x--);
}


//延时函数
void nop_(unsigned char i)
{
    
    
	unsigned char x, y;
	for(x=0;x<i;i++)
		for(y=0;y<100;y++);
}


//i2c初始化
void init()
{
    
    
	sda = 1;
	scl = 1;
}


//开始信号函数
void start()
{
    
    
	scl=1;
	sda=1;
	delay(2);
	sda=0;
	delay(2);
	scl=0;
}



//停止信号函数
void stop()
{
    
    
	sda=0;
	scl=1;
	delay(2);
	sda=1;
	delay(2);
}


//发送应答信号
void SendACK(bit ack)

{
    
    
    scl=0;
    delay(2);
    sda = ack;
    scl = 1;
    delay(2);
    scl = 0;
    delay(2);
}


//等待从机应答
void respons()
{
    
    
	unsigned char i; 
	scl=1;
	while((sda==1)&&(i<250)) 
	{
    
    	
		i++;
	}
	scl=0;
	delay(2);
}


//向总线写一字节
void write_byte(unsigned char dat)
{
    
    
	unsigned char i;

	for(i=0;i<8;i++)
	{
    
    
		scl=0;
		dat<<=1;
		sda=CY;
		scl=1;
		delay(2);
		scl=0;
		delay(2);
	}
	sda=1;
	delay(2);
}	


//从总线读一字节
unsigned char read_byte()
{
    
    
	unsigned char i,j,k;
	scl=0;
	delay(2);
	for(i=0;i<8;i++)
	{
    
    
		scl=1;
		delay(2);
		j=sda;
		k=(k<<=1)|j;
		delay(2);
		scl=0;
		delay(2);
	}
	return k;
}


//向从机写数据
void I2C_write(unsigned char device_add, unsigned char in_add, unsigned char dat)//(设备地址,设备寄存器地址,数据)
{
    
    
	init();
	start();
	write_byte(device_add);
	respons();
	write_byte(in_add);
	respons();
	write_byte(dat);
	respons();
	stop();
}


//读从机的数据
unsigned char I2C_read(unsigned char device_add, unsigned char in_add)//(设备地址,设备寄存器地址)
{
    
    
	unsigned char dat;
	init();
	start();
	write_byte(device_add);
	respons();
	write_byte(in_add);
	respons();
	
	start();
	write_byte(device_add+1);
	respons();
	dat = read_byte();
	SendACK(1);
	stop();
	
	return dat;
}
#endif

i2c.c

#include<reg52.h>
#include<i2c.h>

void main()
{
    
    
	I2C_write(0xa0, 2, 0xcd);
	I2C_write(0xa0, 3, 0xaa);
	
	while(1)
	{
    
    
		P1 = I2C_read(0xa0,2); //读到数据点亮P1总线上的灯\
                                        查看效果
		nop_(200);
		P1 = I2C_read(0xa0,3);
		nop_(200);
	}
}

猜你喜欢

转载自blog.csdn.net/hjskj/article/details/112062826