蓝桥杯之单片机设计与开发(15)——I2C总线通信

 

OK,来到了重难点。

有关于I2C总线信号时序总结请参考https://blog.csdn.net/Xiaomo_haa/article/details/87902379

I2C寻址模式

I2C通信的起始信号之后,首先要发送一个从机地址,这个地址一共有7位,紧跟着第8位是数据方向位(#R/W),“0”表示接下来要发送数据(写),“1”表示接下来是请求数据(读)。

我们蓝桥杯单片机开发与设计的开发板所使用的E2PROM器件型号是AT24C02,我们查询datasheet可得

24C02的七位地址中,高四位是固定的0b1010,低三位的地址取决于具体的电路设计,由芯片上的A2、A1、A0决定。

如我们开发板的原理图:

在这里,24C02的7位地址实际上就是0b1010000,也就是0x50。

在比赛的时候提供的有I2C的代码,我现在直接给粘贴过来。

/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "sys.h"
#include "intrins.h"

#define somenop {_nop_(); _nop_(); _nop_(); _nop_(); _nop_();}    

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

//总线启动条件
void IIC_Start(void)
{
	SDA = 1;
	SCL = 1;
	somenop;
	SDA = 0;
	somenop;
	SCL = 0;	
}

//总线停止条件
void IIC_Stop(void)
{
	SDA = 0;
	SCL = 1;
	somenop;
	SDA = 1;
}

//应答位控制
void IIC_Ack(bit ackbit)
{
	if(ackbit) 
	{	
		SDA = 0;
	}
	else 
	{
		SDA = 1;
	}
	somenop;
	SCL = 1;
	somenop;
	SCL = 0;
	SDA = 1; 
	somenop;
}

//等待应答
bit IIC_WaitAck(void)
{
	SDA = 1;
	somenop;
	SCL = 1;
	somenop;
	if(SDA)    
	{   
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else  
	{ 
		SCL = 0;
		return 1;
	}
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{   
		if(byt&0x80) 
		{	
			SDA = 1;
		}
		else 
		{
			SDA = 0;
		}
		somenop;
		SCL = 1;
		byt <<= 1;
		somenop;
		SCL = 0;
	}
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
	unsigned char da;
	unsigned char i;
	
	for(i=0;i<8;i++)
	{   
		SCL = 1;
		somenop;
		da <<= 1;
		if(SDA) 
		da |= 0x01;
		SCL = 0;
		somenop;
	}
	return da;
}

在这里我们还要注意一点,就是在程序中有个somenop宏定义函数,这里使用了5个nop操作。一个_nop_()操作的时间就是一个机器周期。

I2C通信分为低俗模式100kb/s、快速模式400kb/s和高速模式3.4Mb/s。因为所有的I2C器件都支持低速,但却未必支持另外两种速度,所以作为通用的I2C程序,我们选择100k这个速度来实现,也就是说实际程序产生的时序必须小于等于100k的时序参数,很明显要求SCL的高低电平持续时间都不段于5us。

在这里还有一个小技巧,就是在发送数据那里,如何将一个字节的数据发送出去。我们可以将IIC_SendByte稍微修改一下。

bit IIC_SendByte(unsigned char dat)
{
    bit ack;    //用于暂存应答位的值
    unsigned char mask;    //用于探测字节内某一位值的掩码变量
    
    for(mask = 0x80; mask != 0; mask >>= 1)    //从高位到低位依次执行
    {
        if((mask & dat) == 0)    //该位的值输出到SDA
            SDA = 0;
        else
            SDA = 1;

        somenop;
        SCL = 1;    //拉高SCL
        somenop;
        SCL = 0;    //再拉低SCL,完成一个位周期
    }
    SDA = 1;        //8位数据发送完毕之后,主机释放SDA,以检测从机应答
    somenop;
    SCL = 1;        //拉高SCL
    ack = SDA;       //读取此时的SDA值,即为从机的应答值
    somenop;
    SCL = 0;        //再拉低SCL完成应答位,并保持住主线

    return ack;    //返回从机应答值
}

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值