#include <reg52.h>
#include <intrins.h>
#define LCDClear() LCDWriteCmd(0x01)
sbit RS = P2^0;
sbit RW = P2^1;
sbit E = P2^2;
sbit BF = P0^7;
sbit SCL = P3^4;
sbit SDA = P3^5;
void delay(unsigned int n)
{
unsigned char i;
while (n--)
for (i = 0; i < 115; i++);
}
void LCDBusyTest(void)
{
E = 0;
RS = 0;
RW = 1;
E = 1;
BF = 1;
while (BF);
E = 0;
}
void LCDWriteCmd(unsigned char cmd)
{
LCDBusyTest();
RS = 0;
RW = 0;
E = 1;
P0 = cmd;
E = 0;
}
void LCDWriteData(unsigned char dat)
{
LCDBusyTest();
RS = 1;
RW = 0;
E = 1;
P0 = dat;
E = 0;
}
void LCDWriteString(char *s)
{
while (*s)
LCDWriteData(*s++);
}
void LCDWriteNumber(unsigned char num)
{
char str[4];
str[0] = num / 100 + '0';
str[1] = num % 100 / 10 + '0';
str[2] = num % 10 + '0';
str[3] = '\0';
if (str[0] == '0')
{
if (str[1] == '0')
LCDWriteString(str + 2);
else
LCDWriteString(str + 1);
}
else
LCDWriteString(str);
}
void LCDInit(void)
{
delay(40);
LCDWriteCmd(0x30);
delay(10);
LCDWriteCmd(0x30);
delay(10);
LCDWriteCmd(0x0c);
delay(1);
LCDWriteCmd(0x01);
}
void I2CStart(void)
{
// SCL为高电平时, SDA必须稳定
// 所以必须先置位SDA, 后置位SCL, 顺序绝不能颠倒过来
SDA = 1;
SCL = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
SDA = 0;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void I2CStop(void)
{
SDA = 0;
SCL = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
SDA = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
// 检查从器件的应答
bit I2CAck(void)
{
bit ack;
SDA = 1;
SCL = 0;
_nop_();
_nop_();
SCL = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
ack = SDA;
SCL = 0;
return !ack;
}
// 主器件主动应答
// 1为应答, 0为非应答
void I2CSendAck(bit ack)
{
SDA = !ack;
SCL = 0;
_nop_();
_nop_();
SCL = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
SCL = 0;
}
bit I2CWrite(unsigned char dat)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
SCL = 0;
SDA = dat & 0x80;
dat <<= 1;
SCL = 1;
_nop_();
_nop_();
}
SCL = 0;
return I2CAck();
}
unsigned char I2CRead(void)
{
unsigned char i;
unsigned char dat = 0;
SDA = 1;
for (i = 0; i < 8; i++)
{
SCL = 1;
dat <<= 1;
if (SDA)
dat |= 1;
SCL = 0;
_nop_();
_nop_();
}
return dat;
}
// 跳转到指定地址
bit EEPGoto(unsigned int addr, bit read)
{
unsigned char cmd = 0xa0;
cmd |= addr >> 7 & 0x06;
if (!I2CWrite(cmd)) // slave address for WRITING
return 0;
if (!I2CWrite(addr & 0xff)) // byte address
return 0;
if (read)
{
I2CStart();
return I2CWrite(cmd | read); // slave address again for reading
}
else
return 1;
}
// 单字节写
bit EEPWriteByte(unsigned int addr, unsigned char dat)
{
bit result = 0;
I2CStart();
if (EEPGoto(addr, 0))
result = I2CWrite(dat);
I2CStop();
if (result)
delay(1); // 等待器件擦写完毕
return result;
}
// 页写
// 返回成功写入的字节数
// 24C08中每页为16字节,一次只能写一页, 如果在0地址处写入20个字符,那么第17~20个字符将会被写回0~3地址处
// 如果在2地址处写入16个字符,那么第15~16个字符就会被写入到0~1地址处
unsigned char EEPWritePage(unsigned int addr, unsigned char dat[], unsigned char size)
{
unsigned char n = 0;
if (size == 0)
return 0;
I2CStart();
if (EEPGoto(addr, 0))
{
while (n < size)
{
if (!I2CWrite(dat[n]))
break;
n++;
}
}
I2CStop();
if (n > 0)
delay(1);
return n;
}
// 选择性读
bit EEPReadByte(unsigned int addr, unsigned char *dat)
{
bit result = 0;
I2CStart();
if (EEPGoto(addr, 1))
{
result = 1;
*dat = I2CRead();
}
I2CSendAck(0);
I2CStop();
return result;
}
// 立即地址读
// 读到整个存储器最后一个字节时会自动跳回0地址
bit EEPReadByteIn(unsigned char slave_addr, unsigned char *dat)
{
bit result = 0;
I2CStart();
if (I2CWrite(slave_addr))
{
result = 1;
*dat = I2CRead();
}
I2CSendAck(0); // NOACK
I2CStop();
return result;
}
// 由选择性读启动连续读
unsigned char EEPReadPage(unsigned int addr, unsigned char dat[], unsigned char size)
{
unsigned char n = 0;
if (size == 0)
return 0;
I2CStart();
if (EEPGoto(addr, 1))
{
while (n < size)
{
if (n != 0)
I2CSendAck(1); // 应答表示主器件要求更多的数据
dat[n] = I2CRead();
n++;
}
}
I2CSendAck(0); // 非应答表示停止发送
I2CStop();
return n;
}
// 由立即地址读启动连续读
unsigned char EEPReadPageIn(unsigned char slave_addr, unsigned char dat[], unsigned char size)
{
unsigned char n = 0;
if (size == 0)
return 0;
I2CStart();
if (I2CWrite(slave_addr))
{
while (n < size)
{
if (n != 0)
I2CSendAck(1);
dat[n] = I2CRead();
n++;
}
}
I2CSendAck(0);
I2CStop();
return n;
}
void test(void)
{
char buf[16];
char ch;
unsigned char i, n;
unsigned char cnt = 0;
/* 测试单字节写 */
// 24C08的地址范围: 0x000~0x3ff
cnt += (unsigned char)EEPWriteByte(0x3fd, 'K');
cnt += (unsigned char)EEPWriteByte(0x3fe, 'e');
cnt += (unsigned char)EEPWriteByte(0x3ff, 'y');
/* 测试单字节读 */
LCDClear();
cnt += (unsigned char)EEPReadByte(0x3fd, &ch);
LCDWriteData(ch);
cnt += (unsigned char)EEPReadByte(0x3fe, &ch);
LCDWriteData(ch);
cnt += (unsigned char)EEPReadByte(0x3ff, &ch);
LCDWriteData(ch);
/* 测试页写 */
cnt += EEPWritePage(0x302, "简体中文abcd1234繁", 18);
// 由于一次只能写一页, 每页16字节
// 所以最后的34被写到了0x300~0x301处, "繁"字把"简"字覆盖了
/* 测试立即地址连续读 */
// 当前地址位于"繁"字后面,也就是0x304处
n = EEPReadPageIn(0xa7, buf, 8); // A2接的是低电平, 且要读的区域为第三区域, 所以参数1的第3位为0, 第2~1位为11
LCDWriteCmd(0x90); // 跳至第二行
for (i = 0; i < n; i++)
LCDWriteData(buf[i]);
cnt += n;
/* 测试立即地址读 */
// 当前地址位于"b"字后面, 也就是0x30c处
LCDWriteData('-');
cnt += (unsigned char)EEPReadByteIn(0xa7, &ch);
LCDWriteData(ch);
cnt += (unsigned char)EEPReadByteIn(0xa7, &ch);
LCDWriteData(ch);
/* 测试选择性连续读 */
n = EEPReadPage(0x300, buf, 16);
LCDWriteCmd(0x88); // 跳至第三行
for (i = 0; i < n; i++)
LCDWriteData(buf[i]);
cnt += n;
LCDWriteCmd(0x98); // 跳至第四行
LCDWriteString("共成功操作了");
LCDWriteNumber(cnt);
LCDWriteString("B");
}
int main(void)
{
LCDInit();
test();
while (1);
}