(十)I2C通信

        本节我们来完成I2C的内容的学习,上一节我们了解的是串口通信,主要是我们的电脑和51单片机的通信,而这一节我们介绍51单片机和他的外设的通信,我们通过向E2prom里面读写一个字节数据来完成本实验。

1. I2C总线

        I2C总线是由于飞利浦公司开发的两线式串行总线,用于连接微控制器及其外围设备,I2C总线只有两根双向数据线,一根SDA数据线,数据线用来表示数据,一根SCL时钟线,用来同步时钟。在一个I2C通信线中,可连接多个I2C设备,每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。

上图就是i2c总线的常用连接方式,其中比较重要的是两根线SDA和SCL都有上拉电阻,所以SDA和SCL都是默认有高电平。

2. I2C总线硬件相关。

        I2C总线通信其实就是通过一系列高低电平和外设交换信息,而这个如何发高低电平其实就是协议的内容,我们的协议规定我们如何完成数据的读取和写入。我们通过在读取E2PRM一个字节的通信过程如下,起始信号,写设备地址,E2PROM给单片机一个应答信号(简称ACK),写寄存器地址,回一个ACK,读设备地址,回一个ACK,读取数据,然后可以直接结束。当然如果我们要读多个字节,前面是一样的,只是我们读取一个数据之后,我们需要主动发送一个ack,然后再读,再发送,一直到你不读的时候直接发送停止信号,有时候有人会说发送no ack,其实no ack发送和不发送都是可以的。最后直接发送结束也就可以了。而我们写一个字节数据到E2PROM中,其过程是起始信号,写设备地址,收ACK,写寄存器地址,收ACK,写数据,收ACK,然后发个结束信号,如果是发送多个字节,前面过程也是一样的,只是继续写数据,然后继续接收ACK。直到不发为止,这个时候还是发送结束信号。可能会疑惑这个寄存器地址不需要改变么,其实是这个芯片内部自己就会寄存器地址自己加1,所以我们不需要一直改变寄存器地址,但是如果其他芯片可能没有这个特性,那么我们就应该去看他们的芯片手册,其实大概的原理和思路出不多。

数据有效性规定

        I2C总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟低电平期间,数据线上才能进行数据发生变化。

起始信号和停止信号

        SCL在高电平期间,SDA由高电平变为低电平就是起始信号,SCL在高电平期间,SDA有低电平变为高电平就是结束信号。

应答信号(ACK信号)

         其实就是本身是高电平,而从机把高电平拉低就表示ack信号,这里顺便说一个其实是由高位开始传输。

3硬件

        我们的板子上使用的是AT24C02(EEPROM)芯片,此芯片具有I2C接口,所以我们可以使用我们51单片机模拟i2c接口来与之通信,硬件如下:

4 软件

        我们通过先写一个字节到E2PROM中,然后关机之后再读一个字节显示到数码管上,完成实验。我们先写一个i2c文件来模拟一下i2c通信。

#include "reg52.h"
#include "i2c.h"
sbit SCL = P2^1;
sbit SDA = P2^0;

void delay_us(int n){
	while(n--);
}

void i2c_start(){
	SDA = 1;
	delay_us(10);
	SCL = 1;
	delay_us(10);
	SDA = 0;
	delay_us(10);

}

void i2c_stop(){
	SDA = 0;
	delay_us(10);
	SCL  = 1;
	delay_us(10);
	SDA = 1;
	delay_us(10);
}

void receive_ack(){
	SDA = 1;
	delay_us(10);
	SCL = 1;
	delay_us(10);
	while(SDA == 1);
	delay_us(10);
	SCL = 0;
	delay_us(10);
}
void gpio_i2c_write(unsigned char byte){
	int i;
	unsigned char temp;
	for(i = 0;i <= 7;i++){
		SCL = 0;
		delay_us(10);
		temp = ((byte >> (7 - i)) & 0x01);
		SDA = temp;
		delay_us(10);
		SCL = 1;
		delay_us(10);
		SCL = 0;
		delay_us(10);	
	}
}	
		   
unsigned char gpio_i2c_read(){
	int i;
	unsigned char temp = 0;
	SCL = 0;
	delay_us(10);
	SDA = 1;
	delay_us(10);
	for(i = 0;i <= 7;i++){
		SCL = 1;
		delay_us(10);
		temp <<= 1;
		temp |= SDA;
		SCL = 0;
		delay_us(10);
	}
	return temp;
}
void i2c_write(unsigned char reg_addr, unsigned char date){
	i2c_start();
	gpio_i2c_write(0xa0);
	receive_ack();
	gpio_i2c_write(reg_addr);
	receive_ack();
	gpio_i2c_write(date);
	receive_ack();
	i2c_stop();
} 
unsigned char i2c_read(unsigned char reg_addr){
	unsigned char date;

	i2c_start();
	gpio_i2c_write(0xa0);
	receive_ack();
	gpio_i2c_write(reg_addr);
	receive_ack();
	i2c_start();
	gpio_i2c_write(0xa0 | 0x01);
	receive_ack();
	date = gpio_i2c_read();
	i2c_stop();

	return date;
}

再把数码管的内容发一下

#include "reg52.h"
unsigned char code smg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
void delay_ms(int xms){
	int i,j;
	for(i = 0;i < xms;i++){
		for(j = 0;j< 100;j++);
	}
}
void key_scan(unsigned char date){
	int i;

	for(i = 0;i < 2;i++){
		switch(i){
			case 0:
				LSA = 0; LSB = 0; LSC = 0;
				P0 = smg[date % 10];
				break;
			case 1:
				LSA = 1; LSB = 0; LSC = 0;
				P0 = smg[date / 10];
				break;
			default:
				break;
		}
		delay_ms(10);
		P0 = 0xff;
		
	}
}

最后发一下调用:

#include "reg52.h"
#include "i2c.h"
#include "key.h"
void main(){
	unsigned char date = 0;
	i2c_write(0,10);
	date = i2c_read(0);

	while(1){
		key_scan(date);
	}
}

这样就完成了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值