djdgf4 发表于 2023-3-19 15:05

51单片机在AT24C02读取数据的时候读不到

本帖最后由 djdgf4 于 2023-3-20 22:12 编辑

写了一个按键控制led移位、存储和读取的程序,反复测试,读取P2的时候总是0x00,。AT24C02器件本身没问题,配置代码也没问题,因为开另一个使用24C02的工程的时候一切正常,可以存储读取
工程如下:

main.c:

#include <REGX52.H>
#include "Delay.h"
#include "AT24C02.h"
#include "key.h"
//#include "MatrixKey.h"

#define uchar unsigned char
#define StorageAddress 0xF1 //数据存储地址

#if 1
void main()
{
      uchar KeyNum;
      uchar LED_shift = 0;
      
      P2 = ~0X01;
      
      while (1)
      {
                KeyNum = key();
                if(KeyNum == 1)//左移
                {      
                        Delay(5);
                        if (LED_shift >= 8)
                        {
                              LED_shift = 0;
                        }
                        P2 = ~(0x01 << ++LED_shift);
                        Delay(5);
                }
                if(KeyNum == 2)//右移
                {
                        Delay(5);
                        if (LED_shift == 0)
                        {
                              LED_shift = 8;
                        }
                        P2 = ~(0x01 << --LED_shift);
                        Delay(5);
                }
                if (KeyNum == 3)
                {
                        AT24C02_SendData(StorageAddress, P2);//把P2存入AT24C02
                        Delay(5);
                }
                if(KeyNum == 4)
                {
                        P2 = AT24C02_ReceiveData(StorageAddress);//从AT24C02读取P2

                }
      }
}
#endif


AT24C02.C:

#include "I2C.h"

#define uchar unsigned char
#define SLAVE_ADDRESS 0XA0
/**
*@brief   AT24C02写入一个字节
*@Param   WordAddress 要写入字节的地址
      *@param   Data 要写入的数据
*@retva   无
*/
void AT24C02_SendData(uchar WordAddress, uchar Data)
{
    I2C_Start();
    I2C_SendByte(SLAVE_ADDRESS);
    I2C_ReceiveACK();
    I2C_SendByte(WordAddress);
    I2C_ReceiveACK();
    I2C_SendByte(Data);
    I2C_ReceiveACK();
    I2C_Stop();
}
/**
*@brief   AT24C02读取一个字节
*@param   WordAddress 要读出字节的地址
*@retva   Data 读出的数据
*/
uchar AT24C02_ReceiveData(uchar WordAddress)
{
    uchar Data;
    I2C_Start();
    I2C_SendByte(SLAVE_ADDRESS);
    I2C_ReceiveACK();
    I2C_SendByte(WordAddress);
    I2C_ReceiveACK();

    I2C_Start();
    I2C_SendByte(SLAVE_ADDRESS | 0x01);
    I2C_ReceiveACK();
    Data = I2C_ReceiveByte();
    I2C_SendACK(1);
    I2C_Stop();
    return Data;
}

I2C.c:

#include <REGX52.H>

sbit I2C_SDA = P2^0;
sbit I2C_SCL = P2^1;

#define uchar unsigned char

/*
*@brief   I2C开始
*@param
*@retva
*/
void I2C_Start()
{
    I2C_SCL = 0;
    I2C_SDA = 1;

    I2C_SCL = 1;
    I2C_SDA = 0;
}

/*
*@brief   I2C停止
*@param
*@retva
*/
void I2C_Stop()
{
    I2C_SDA = 0;
    I2C_SCL = 1;

    I2C_SDA = 1;
}

/*
*@brief   I2C发送一个字节
*@param   Byte 要发送的字节
*@retva
*/
void I2C_SendByte(uchar Data)
{
    uchar i;
    I2C_SDA = 0; // 在接收前释放SDA
    for (i = 0; i < 8; i++)
    {
      I2C_SCL = 0;
      I2C_SDA = Data;
      I2C_SCL = 1;
    }
}

/*
*@brief   I2C接受一个字节
*@param   无
*@retva   Byte 接收到的字节数据
*/
uchar I2C_ReceiveByte()
{
    uchar Data;
    uchar i;
    I2C_SDA = 0;
    for (i = 0; i < 8; i++)
    {
      I2C_SCL = 0;
      Data = I2C_SDA;
      I2C_SCL = 1;
    }
    return Data;
}

/*
*@brief   I2C发送应答
*@param   ACKBit 应答位,0为应答,1为非应答
*@retva
*/
void I2C_SendACK(bit ACK_Sending)
{
    I2C_SCL = 0;
    I2C_SDA = ACK_Sending;
    I2C_SCL = 1;
}

/*
*@brief   I2C接受应答位
*@param   无
*@retva   ACKBit 接收到的应答位
*/
uchar I2C_ReceiveACK()
{
    bit ACK_Receiving;
    I2C_SCL = 0;
    ACK_Receiving = I2C_SDA;
    I2C_SCL = 1;
    return ACK_Receiving;
}

经过调试,发现在从写入AT24C02函数 AT24C02_SendData 出来的时候数据就错了,再仔细调试发现,在I2C_ReceiveByte函数结束的时候,data变成了0x00。
经过比对,发现整个I2C的具体实现都有~瑕疵~,重新改写I2c如下,烧录,完美解决。
#include <REGX52.H>

sbit I2C_SDA = P2^0;
sbit I2C_SCL = P2^1;

#define uchar unsigned char

/*
*@brief   I2C开始
*@param
*@retva
*/
void I2C_Start()
{
    I2C_SDA = 1;

    I2C_SCL = 1;
    I2C_SDA = 0;
    I2C_SCL = 0;
}

/*
*@brief   I2C停止
*@param
*@retva
*/
void I2C_Stop()
{
    I2C_SDA = 0;
    I2C_SCL = 1;

    I2C_SDA = 1;
}

/*
*@brief   I2C发送一个字节
*@param   Byte 要发送的字节
*@retva
*/
void I2C_SendByte(uchar Data)
{
    uchar i;
    //I2C_SDA = 0; // 在接收前释放SDA,释放SDA,就是将IO置1
    /*这句没有必要*/
    for (i = 0; i < 8; i++)
    {
      I2C_SDA = Data & (0x80 >> i);
      I2C_SCL = 1;
      I2C_SCL = 0;
    }
}

/*
*@brief   I2C接受一个字节
*@param   无
*@retva   Byte 接收到的字节数据
*/
uchar I2C_ReceiveByte()
{
    uchar Data = 0;
    uchar i;
    I2C_SDA = 1;// 在接收前释放SDA
    for (i = 0; i < 8; i++)
    {
      //Data = I2C_SDA;傻逼,写错了
      I2C_SCL = 1;
      if (I2C_SDA)
      {
            Data |= (0x80 >> i);
      }
      I2C_SCL = 0;
    }
    return Data;
}

/*
*@brief   I2C发送应答
*@param   ACKBit 应答位,0为应答,1为非应答
*@retva
*/
void I2C_SendACK(bit ACK_Sending)
{
    //I2C_SCL = 0;这句没必要
    I2C_SDA = ACK_Sending;
    I2C_SCL = 1;
    I2C_SCL = 0;
}

/*
*@brief   I2C接受应答位
*@param   无
*@retva   ACKBit 接收到的应答位
*/
uchar I2C_ReceiveACK()
{
    bit ACK_Receiving;
    I2C_SDA= 1;// 在接收前释放SDA
    I2C_SCL = 1;
    ACK_Receiving = I2C_SDA;
    I2C_SCL = 0;
   
    return ACK_Receiving;
}


> I2C总线释放SDA是将IO口置1的原因是:
>
> I2C总线的IO口是开漏或开集电极模式,即只能输出低电平或高阻态。
> 当IO口为高阻态时,SDA可以被其他设备拉高或拉低,即释放了对总线的控制权。
> SDA和SCL都为高电平时,表示总线空闲或结束信号。

dongge666 发表于 2023-3-19 16:15

换用其他io口试试呢

ciker_li 发表于 2023-3-19 16:36

单独写给读取P2的程序看看,是不是P2口虚焊了

shicoco 发表于 2023-3-19 20:05

很厉害的样子还行吧

nbacba456456 发表于 2023-3-19 20:09

很厉害的样子还行吧

djdgf4 发表于 2023-3-20 22:13

zhujue 发表于 2023-3-19 17:08
大佬这么牛,不顶不行啊,代码也很详尽。可以抄作业啦

代码已经改过来了
页: [1]
查看完整版本: 51单片机在AT24C02读取数据的时候读不到