本帖最后由 djdgf4 于 2023-3-20 22:12 编辑
写了一个按键控制led移位、存储和读取的程序,反复测试,读取P2的时候总是0x00,。AT24C02器件本身没问题,配置代码也没问题,因为开另一个使用24C02的工程的时候一切正常,可以存储读取
工程如下:
main.c:
[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:
[C] 纯文本查看 复制代码 #include "I2C.h"
#define uchar unsigned char
#define SLAVE_ADDRESS 0XA0
/**
* [url=home.php?mod=space&uid=190858]@brief[/url] AT24C02写入一个字节
* [url=home.php?mod=space&uid=952169]@Param[/url] 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:
[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如下,烧录,完美解决。
[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_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都为高电平时,表示总线空闲或结束信号。
|