现象:单个读取字符正确,连续读取字符出错环境:stc89c52单片机 24C02的epprom
我的解决办法:
IIC_wirte 8bit时等待ack,如无ack,则stop-->start 重新发起信号,结果解决问题,下面贴出代码:
- #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
单片机交流学习群: |