IIC连续读写出错检查办法

单片机连续读写出错###

现象:读取单个字节的时候是正确的,但连续读或连续写会出错
经过排查,
1、发现单片机连续读epprom时,没有回应ack
2、start --> send 器件地址写–> epprom存储地址 --> 器件地址读 时,中间有步骤出错

#include <reg52.h>
#include "intrins.h"
#include "type.h"
#include "i2c.h"

sbit SCL=P2^1;
sbit SDA=P2^0;

#define IIC_WRITE 0xA0
#define IIC_READ 0xA1

static void delay10Us(u16 i)
{
	while(i--);
}

/*******************************************************************************
* 函数名         : Delay10us()
* 函数功能		   : 延时10us
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
/*
void Delay10us()
{
}
*/
/*******************************************************************************
* 函数名         : I2cStart()
* 函数功能		 : 起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿
* 输入           : 无
* 输出         	 : 无
* 备注           : 起始之后SDA和SCL都为0
*******************************************************************************/

void I2cStart()
{
	SCL=0;
	delay10Us(1);
	SDA=1;
	SCL=1;
	delay10Us(1);//建立时间是SDA保持时间>4.7us
	SDA=0;
	delay10Us(1);//保持时间是>4us
}
/*******************************************************************************
* 函数名         : I2cStop()
* 函数功能		 : 终止信号:在SCL时钟信号高电平期间SDA信号产生一个上升沿
* 输入           : 无
* 输出         	 : 无
* 备注           : 结束之后保持SDA和SCL都为1;表示总线空闲
*******************************************************************************/

void I2cStop()
{
	SCL=0;
	delay10Us(1);
	SDA=0;
	SCL=1;
	delay10Us(1);
	SDA=1;
	delay10Us(1);
}
/*******************************************************************************
* 函数名         : I2cSendByte(unsigned char dat)
* 函数功能		 : 通过I2C发送一个字节。在SCL时钟信号高电平期间,保持发送信号SDA保持稳定
* 输入           : num
* 输出         	 : 0或1。发送成功返回1,发送失败返回0
* 备注           : 发送完一个字节SCL=0,SDA=1
*******************************************************************************/

unsigned char I2cSendByte(unsigned char dat)
{
	unsigned char a=0,b=0;//最大255,一个机器周期为1us,最大延时255us。	
	SCL=0;
	delay10Us(1);
	for(a=0;a<8;a++)//要发送8位,从最高位开始
	{
		SDA = dat>>7;	 //起始信号之后SCL=0,所以可以直接改变SDA信号
		dat = dat<<1;
		delay10Us(1);
		SCL=1;
		delay10Us(2);
		SCL=0;
		delay10Us(1);		
	}
	SDA=1;
	delay10Us(1);
	SCL=1;
	while(SDA)//等待应答,也就是等待从设备把SDA拉低
	{
		b++;
		if(b>200)	 //如果超过2000us没有应答发送失败,或者为非应答,表示接收结束
		{
			return 0;
		}
		delay10Us(1);
	}
	
 	return 1;		
}
/*******************************************************************************
* 函数名         : I2cReadByte()
* 函数功能		   : 使用I2c读取一个字节
* 输入           : 无
* 输出         	 : dat
* 备注           : 接收完一个字节SCL=0,SDA=1.
*******************************************************************************/

unsigned char I2cReadByte()
{
	unsigned char a=0,dat=0;
	SCL = 0;
	delay10Us(1);
	SDA=1;			//起始和发送一个字节之后SCL都是0
	delay10Us(1);
	for(a=0;a<8;a++)//接收8个字节
	{
		SCL=1;
		delay10Us(2);
		dat<<=1;
		dat|=SDA;
		SCL=0;
		delay10Us(1);
	}
	return dat;		
}


/*******************************************************************************
* 函数名         : void At24c02Write(unsigned char addr,unsigned char dat)
* 函数功能		   : 往24c02的一个地址写入一个数据
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void At24c02Write(unsigned char addr,unsigned char dat)
{
	unsigned char ret;
again:
	I2cStart();
	ret = I2cSendByte(IIC_WRITE);//发送写器件地址
	if(!ret){
		I2cStop();
		goto again;
	}
	ret = I2cSendByte(addr);//发送要写入内存地址
	if(!ret){
		I2cStop();
		goto again;
	}
	ret = I2cSendByte(dat);	//发送数据
	if(!ret){
		I2cStop();
		goto again;
	}
	I2cStop();
}

/*******************************************************************************
* 函数名         : unsigned char At24c02Read(unsigned char addr)
* 函数功能		   : 读取24c02的一个地址的一个数据
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

unsigned char At24c02Read(unsigned char addr)
{
	unsigned char num;
	unsigned char ret;
again:
	I2cStart();
	ret = I2cSendByte(IIC_WRITE); //发送器件写地址
	if(!ret){
		I2cStop();
		goto again;
	}
	ret = I2cSendByte(addr); //发送要读取的地址
	if(!ret){
		I2cStop();
		goto again;
	}
	I2cStart();
	ret = I2cSendByte(IIC_READ); //发送读器件地址
	if(!ret){
		I2cStop();
		goto again;
	}
	num=I2cReadByte(); //读取数据
	NoAck();
	I2cStop();
	return num;	
}

/*
非应答
*/
void NoAck(void)
{
	SCL=0;
	SDA=1;
	delay10Us(1);
	SCL=1;
	delay10Us(2);
}

/*
应答信号
*/
void Ack(void)
{
	SCL=0;
	SDA=0;
	delay10Us(1);
	SCL=1;
	delay10Us(2);
}
/*******************************************************************************
* 函数名         : unsigned char At24c02Read(unsigned char addr)
* 函数功能		   : 读取24c02的一个地址的一个数据
* 输入           : 无
* 输出         	 : -1:error 0:success
*******************************************************************************/

void At24c02ReadBytes(unsigned char addr,unsigned char *dat,unsigned char n)
{
	//unsigned char num = n;
	unsigned char ret;
again:
	I2cStart();
	ret=I2cSendByte(0xa0); //发送写器件地址
	if(!ret){
		I2cStop();
		goto again;
	}
	ret=I2cSendByte(addr); //发送要读取的地址
	if(!ret){
		I2cStop();
		goto again;
	}
	I2cStart();
	ret=I2cSendByte(0xa1); //发送读器件地址
	if(!ret){
		I2cStop();
		goto again;
	}
	if(addr + n >256) 
	{
		n = 256 - addr;
	}
	while(n--) 
	{
		*dat = I2cReadByte(); //读取数据
		if(n!=0) Ack();
		dat++;
	}
	I2cStop();
	//return num;	
}

/*******************************************************************************
* 函数名         : unsigned char At24c02Read(unsigned char addr)
* 函数功能		   : 读取24c02的一个地址的一个数据
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

/*******************************************************************************
* 函数名         : void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
* 函数功能		 : 从地址addr开始,连续读取n个字符,读取字符放入dat中
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
{
	unsigned char i;
	for(i=0;i<n;i++)
		dat[i]= At24c02Read(addr+i);
}
/*******************************************************************************
* 函数名         : void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
* 函数功能		 : 将dat中的字符写入addr开始的epprom中去,写入n个
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void At24c02WriteBytes(unsigned char addr,unsigned char *dat,unsigned char n)
{
	unsigned char i;
	for(i=0;i<n;i++)
	{
		At24c02Write(addr+i,dat[i]);
	}
}

上述代码的关键点在 I2cSendByte 这个方法有返回值,当I2cSendByte的返回值是1说明成功
1、在At24c02Write函数中利用goto语句来处理写出错;
2、At24c02Read 利用goto语句来处理地址写出错
3、At24c02ReadBytes 同上
总结:
1、在iic的连续read过程一定要有ACK
2、起始信号之后的器件地址写,存储器地址、器件地址读 这三个地址时,确保收到epprom发来的ack

广而告之:本人在上海开有单片机培训中心,同时承接单片机开发项目

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值