最近在做一个点阵屏的项目,其中用到了储存芯片20c08,当找资料时时发现全网都是24c02的读写例程然后程序下进去后发现能用,就没有在意(开始测试的时候数据用的很少不到256个)。
当程序框架基本完成时我开始进行多数测试,突然发现我的程序只能进行0-255的数据存储,当数据超过255时就会重头开始。
后来我从网上找了很多资料发现都是24c02的程序,存储数量都不能超过256。于是经过一天的研究终于解决了这个问题。记录一下,希望能帮助与到同样问题的小伙伴。
第一部分 原理说明
在读写过程中24c0x是分页写的,每页可以存256个字节,网上的驱动程序多为24c02,默认为第一页,当输入大于256时需要加一个换页,以24c08为例,一共有1K字节,每页256,一共为4页,以下为I²C的发送命令格式,前面四个1010是eeprom的固定格式不能动,对应16进制为A;后面四位的前三位数据(P0,P1,P2)就是翻页所需的地址(也可以理解为页地址),第四位R/W为读写控制位,最重要的就是图中的P0,P1,P2数据位,因为24c08只有四页所以只需要两个二进制数据即可(P2位强制为0)
P1 P2值 | |
00 | 第0页 |
01 | 第1页 |
10 | 第2页 |
00 | 第3页 |
第二部分 程序编写
我的程序并没有从头开始写,由于我是在24c02的基础上搭好了程序框架才发现的问题,所以我的程序修改宗旨就是:以最少的修改完成读写任务,
下面是我原本的24C02程序
#include<reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit sda = P2 ^ 1; //IO口定义
sbit scl = P2 ^ 0;
//此为待写入24c02的数据。为了便于验证结果,数组的内容为周期重复的。
void UART_TX_Send(uchar i);
char code music[] = {
0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0,
0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0
}; //由于最后还要讲这些数据通过串口发送到电脑 波特率9600
uchar data buffer[100]; //用于缓存从24c02中读取的数据。
void delay(unsigned int m)
{
unsigned int n, p;
for (n = m; n>0; n--)
for (p = 125; p>0; p--);
}
void nop()
{
_nop_();
_nop_();
}
/24C02读写驱动程序
void delay1(unsigned int m)
{
unsigned int n;
for (n = 0; n<m; n++);
}
void init() //24c02初始化子程序
{
scl = 1;
nop();
sda = 1;
nop();
}
void start() //启动I2C总线
{
sda = 1;
nop();
scl = 1;
nop();
sda = 0;
nop();
scl = 0;
nop();
}
void stop() //停止I2C总线
{
sda = 0;
nop();
scl = 1;
nop();
sda = 1;
nop();
}
void writebyte(unsigned char j) //写一个字节
{
unsigned char i, temp;
temp = j;
for (i = 0; i<8; i++)
{
temp = temp << 1;
scl = 0;
nop();
sda = CY; //temp左移时,移出的值放入了CY中
nop();
scl = 1; //待sda线上的数据稳定后,将scl拉高
nop();
}
scl = 0;
nop();
sda = 1;
nop();
}
unsigned char readbyte() //读一个字节
{
unsigned char i, j, k = 0;
scl = 0; nop(); sda = 1;
for (i = 0; i<8; i++)
{
nop(); scl = 1; nop();
if (sda == 1)
j = 1;
else
j = 0;
k = (k << 1) | j;
scl = 0;
}
nop();
return(k);
}
void clock() //I2C总线时钟
{
unsigned char i = 0;
scl = 1;
nop();
while ((sda == 1) && (i<255))
i++;
scl = 0;
nop();
}
从24c02的地址address中读取一个字节数据/
unsigned char read24c02(unsigned char address)
{
unsigned char i;
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
start();
writebyte(0xa1);
clock();
i = readbyte();
stop();
delay1(100);
return(i);
}
//向24c02的address地址中写入一字节数据info/
void write24c02(unsigned char address, unsigned char info)
{
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
writebyte(info);
clock();
stop();
delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
}
//从24c02的地址address中读取一个字节数据/
//串口初始化
void UARTInit()
{
EA = 1; //打开总中断
ES = 1; //打开串口中断
SM0 = 0;
SM1 = 1;//串口工作方式1,8位UART波特率可变
REN = 1;//串口允许接收
TR1 = 1;//启动定时器1
TMOD |= 0x20;//定时器1,工作模式2 8位自动重装
TH1 = 0xfd;
TL1 = 0xfd;//设置比特率9600
}
//串口发送程序
void UART_TX_Send(uchar i)
{
SBUF=i;
while(!TI);
TI=0;
}
void main()
{
uchar add, j;
init(); //初始化24C02
UARTInit();
while (add != sizeof(music))
{
write24c02(0x05 + add, music[add]);
add++;
if (add % 4 == 0)
P1 = ~P1;
}
//到此为止,向24C02中写入数据的过程均已结束。下面的程序为附加的。
//将已写入的数据再读出,送到串口助手
for (j=0;j<sizeof(music);j++)
{
UART_TX_Send(read24c02(0x05+j));
delay(50);
}
// while (j != add)
// {
//
// UART_TX_Send(read24c02(0x05+j));
// j++;
// delay(50);
// }
while (1);
}
经过分析后我仅仅修改了读写函数 以下是修改部分
主函数测试 直接从0xf5开始读写 最后打印的数据正常
这个改法好处是其他部分的程序都不用动,直接往里面扔数据就行大于256也可以。
下面是改好的程序
#include<reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit sda = P2 ^ 1; //IO口定义
sbit scl = P2 ^ 0;
//此为待写入24c02的数据。为了便于验证结果,数组的内容为周期重复的。
void UART_TX_Send(uchar i);
char code music[] = {
0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0,
0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0, 0x55, 0xaa, 0x0f, 0xf0
}; //由于最后还要讲这些数据通过串口发送到电脑 波特率9600
uchar data buffer[100]; //用于缓存从24c02中读取的数据。
void delay(unsigned int m)
{
unsigned int n, p;
for (n = m; n>0; n--)
for (p = 125; p>0; p--);
}
void nop()
{
_nop_();
_nop_();
}
/24C02读写驱动程序
void delay1(unsigned int m)
{
unsigned int n;
for (n = 0; n<m; n++);
}
void init() //24c02初始化子程序
{
scl = 1;
nop();
sda = 1;
nop();
}
void start() //启动I2C总线
{
sda = 1;
nop();
scl = 1;
nop();
sda = 0;
nop();
scl = 0;
nop();
}
void stop() //停止I2C总线
{
sda = 0;
nop();
scl = 1;
nop();
sda = 1;
nop();
}
void writebyte(unsigned char j) //写一个字节
{
unsigned char i, temp;
temp = j;
for (i = 0; i<8; i++)
{
temp = temp << 1;
scl = 0;
nop();
sda = CY; //temp左移时,移出的值放入了CY中
nop();
scl = 1; //待sda线上的数据稳定后,将scl拉高
nop();
}
scl = 0;
nop();
sda = 1;
nop();
}
unsigned char readbyte() //读一个字节
{
unsigned char i, j, k = 0;
scl = 0; nop(); sda = 1;
for (i = 0; i<8; i++)
{
nop(); scl = 1; nop();
if (sda == 1)
j = 1;
else
j = 0;
k = (k << 1) | j;
scl = 0;
}
nop();
return(k);
}
void clock() //I2C总线时钟
{
unsigned char i = 0;
scl = 1;
nop();
while ((sda == 1) && (i<255))
i++;
scl = 0;
nop();
}
unsigned int read24c02(unsigned int address)
{
uchar i,addr;
addr=(address/0xff*2); //address 0 1 2 3, addr 0 2 4 6
//UART_TX_Send(addr);
start();
writebyte(0xa0+addr);//选页 写:第1页A0 ,2页A2 ,3页A4,4页A6
clock();
writebyte(address%0xff);//写地址0-255
clock();
start();
writebyte(0xa1+addr);///选页 读:第1页A1 ,2页A3 ,3页A4,4页A7 就是写+1
clock();
i = readbyte();
stop();
delay1(100);
return(i);
}
//向24c02的address地址中写入一字节数据info/
void write24c02(unsigned int address, unsigned char info)
{
uchar addr;
addr=address/0xff*2; //address/0xff= 0 1 2 3, addr= 0 2 4 6
start();
writebyte(0xa0+addr);//选页 写:第1页A0 ,2页A2 ,3页A4,4页A6
clock();
writebyte(address%0xff);//写地址0-255
clock();
writebyte(info);
clock();
stop();
delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
}
//从24c02的地址address中读取一个字节数据/
//串口初始化
void UARTInit()
{
EA = 1; //打开总中断
ES = 1; //打开串口中断
SM0 = 0;
SM1 = 1;//串口工作方式1,8位UART波特率可变
REN = 1;//串口允许接收
TR1 = 1;//启动定时器1
TMOD |= 0x20;//定时器1,工作模式2 8位自动重装
TH1 = 0xfd;
TL1 = 0xfd;//设置比特率9600
}
//串口发送程序
void UART_TX_Send(uchar i)
{
SBUF=i;
while(!TI);
TI=0;
}
void main()
{
uchar add, j;
init(); //初始化24C02
UARTInit();
while (add != sizeof(music))
{
write24c02(0xf5 + add, music[add]);
add++;
if (add % 4 == 0)
P1 = ~P1;
}
//到此为止,向24C02中写入数据的过程均已结束。下面的程序为附加的。
//将已写入的数据再读出,送到串口助手
for (j=0;j<sizeof(music);j++)
{
UART_TX_Send(read24c02(0xf5+j));
delay(50);
}
while (1);
}