单片机连续读写出错###
现象:读取单个字节的时候是正确的,但连续读或连续写会出错
经过排查,
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
广而告之:本人在上海开有单片机培训中心,同时承接单片机开发项目