对于以上问题,我们想大家都有不同的看法,但是,不知道您是否真正尝试过呢?能正确读取AT24C01到AT24C1024之间的各种型号吗?
为了解决这个问题,本人思考了好几天,以通过多种实验去验证,今天终于获取结果了,不过本人先声明,我这里只有3中类型的卡(C02, C16, C64),每种类型有2张或以上,验证都是正确的。如果你有其他类型的卡不妨也试试,如果尝试了,请把结果告诉本人,在下先谢了。
下面我们先谈谈这类芯片的一些基础知识,得到这些基础知识后,看您能否想出解决这个问题的方法,再看看方法是否和我一样的。
虽然这一章中讲的芯片不是ATMEL的,但是和这一系列完全兼容。 1. 参数表 2. 器件地址表 3. 数据地址表 4. 操作时序 |
1. 不同型号其容量不同;
2. 不同型号的页写入不同;
3. 不同型号的扩展数量不同。
还可以看出,对于24C01/02/04/08/16的数据地址只有1字节,而24C32/64/128/256等的数据地址为两字节。我们仔细想想发现,1字节的数据地址对于24C01/02刚好够用,而对于24C04/08/16来说却不够用,所以,还必须配合器件地址实现读写操作。
至此,您是否想出分别型号的方法?
我们知道AT24C01的最大容量为1Kbit,以就是128字节,如果我们读写128以后的地址不正确,我们就可以确定这个芯片的型号就为AT24C01了。如果用同样的方法,从大到小的访问,应该就可以区分这一系列芯片的不同型号了。
有了想法,那我们不妨试试吧。。。。。。。
结果如何呢?你是否猜到了?
。。。
通过验证我们可以获得结论:不管是什么型号的芯片都可以正确读写,根本无法分辨这一系列。24C01/02以及24C32/64/128/256无法通过24C04/08/16的程序,但其他程序都可以操作。而24C04/08/16可以通过全部型号的读写操作。
也就是说:我们只能把这一系列芯片分为两大类,而无法分辨其型号。
奇怪,这个 问题是怎样产生的呢?
查看芯片资料我们不难发现,如果读写操作超过芯片地址,它是不会返回错误的,而是地址回卷,又从最小的地址开始,所以,就是你写入地址超过芯片范围也无法获取错误。
至于24C04/08/16能够通过各个型号芯片的读写程序,是以为,这三个芯片的地址有特殊性。在读写超过8位地址的地方是通过与页地址配合实现的,以就是说这三个芯片的地址是由:0xA* + 8Bit构成;而24C01/02的地址是由0xA0 + 8位地址构成;24C32/64/128/256的地址是由:0xA0 + 16位地址构成。
由这三个地址可以看出,由于24C04/08/16支持0xA*地址,所以可以通过各种格式的读取,而其他两类不支持0xA0以外的地址,所以当通过24C04/08/16程序读写这两类芯片时就会出现错误。当然24C04/08/16这三个芯片的地址由有些区别,例如04的只有1位,08的有2位,16的有3位,我们可以通过程序进一步区分这三个型号。
我们知道,不同型号其页大小是有区别的,当操作超过页面时,芯片或滚动覆盖,我们可以通过写入最大页面数据,根据读取的数据可以知道其滚动状态,从而读取芯片页面大小。
但是,AT24C01的页字节为8,AT24C02/04/08/16的页字节为16,AT24C32/64的页字节为32,AT24C128/256的页字节为64,所以我们只能分出这4类芯片,还是无法实现所有型号的判别。
例如:同时在芯片的最后几个空间内同时写入4字节的数据:
- u8 tmpBuf[] = {0x00, 0x00, 0x00, 0x00};
- u8 tmpDat[] = {0xAA, 0x55, 0xFF, 0x00};
- ATReadDat(type, addr-3, tmpBuf, 4); // 数据暂存
- ATWriteDat(type, addr-3, tmpDat, 4); // 写入验证数据
- memset(tmpDat, 0, 4);
- ATReadDat(type, addr-3, tmpDat, 4); // 读取验证数据
- ATWriteDat(type, addr-3, tmpBuf, 4); // 恢复写入前
- return ((memcmp(tmpDat, "\xAA\x55\xFF\x00", 4) == 0) ? 0x00 : 0x01);
进一步修改代码:
我们知道,由于芯片超地址时会出现覆盖写入,那么我们能不能把基础可能会出现覆盖的地方写入不同值了,如果发现覆盖就可以说明这个型号是错的,如果没有覆盖就说这个型号是对的:
- u8 tmpBuf[] = {0x00, 0x00};
- u8 tmpDat[] = {0xAA, 0x55};
- ATReadDat(type, ((addr+1)/2)-1, &tmpBuf[0], 1); // 数据暂存
- ATReadDat(type, addr, &tmpBuf[1], 1);
- ATWriteDat(type, ((addr+1)/2)-1, &tmpDat[0], 1);
- ATWriteDat(type, addr, &tmpDat[1], 1); // 写入验证数据
- memset(tmpDat, 0, 2);
- ATReadDat(type, ((addr+1)/2)-1, &tmpDat[0], 1);
- ATReadDat(type, addr, &tmpDat[1], 1); // 读取验证数据
- ATWriteDat(type, ((addr+1)/2)-1, &tmpBuf[0], 1); // 恢复写入前
- ATWriteDat(type, addr, &tmpBuf[1], 1);
- return ((memcmp(tmpDat, "\xAA\x55", 2) == 0) ? 0x00 : 0x01);
- u8 tmpBuf[] = {0x00, 0x00, 0x00, 0x00};
- u8 tmpDat[] = {0xAA, 0x55, 0xFF, 0x00};
- ATReadDat(type, ((addr+1)/2)-2, &tmpBuf[0], 2); // 数据暂存
- ATReadDat(type, addr-1, &tmpBuf[2], 2);
- ATWriteDat(type, ((addr+1)/2)-2, &tmpDat[0], 2);
- ATWriteDat(type, addr-1, &tmpDat[2], 2); // 写入验证数据
- memset(tmpDat, 0, 4);
- ATReadDat(type, ((addr+1)/2)-2, &tmpDat[0], 2);
- ATReadDat(type, addr-1, &tmpDat[2], 2); // 读取验证数据
- ATWriteDat(type, ((addr+1)/2)-2, &tmpBuf[0], 2); // 恢复写入前
- ATWriteDat(type, addr-1, &tmpBuf[2], 2);
- return ((memcmp(tmpDat, "\xAA\x55\xFF\x00", 4) == 0) ? 0x00 : 0x01);
至于为什么这样写可以实现,本人也还没有一个完整的理论依据,大家不妨一起想想,如果你先想出来,请告诉我一下。
下面我们举例说明:
用上面的程序,我们判别型号的顺序是256->128->64->32->16->08->04->02->01,写入的地址是芯片的最大地址的最后两个字节和芯片最大地址的一半的最后两个字节,这样做的目的是试图通过数据覆盖来判断型号,例如,如果最大地址的最后两个字节覆盖了一半的最后两个字节,固然不是这个型号。
所以在按照24c256来想AT24C02写入数据是,我们是通过在地址:16382(0x3FFE)写入两个字节(0xAA, 0x55),再在地址32766(7FFE)写入两个字节(0xFF, 0x00),通过读取整片AT24C02芯片获得:
[48] = 0xAA,
[49] = 0x55,
[63] = 0xFE,
[112] = 0xFF,
[113] = 0x00,
[127] = 0xFE,
通过仔细分析我们发现,芯片处理时,首先把地址16382(0x3FFE)分为2字节处理,高字节为地址即63(0x3F),低字节为数据0xFE,再加上AT24C02的页面大小为16字节,地址0x3F已经是页面的最高地址,后面再写入数据时就会发生页面翻转现象,而页面的起始地址正好是48(0x30),故而后面发生的两字节数据就写入了48和49.
后面两个字节的数据也是完全一致的现象,所以,这个程序破坏来原始数据。
下面我们谈谈另外一思路:通过页和地址来实现。
我们知道AT24C01为8字节一页, AT24C02/04/08/16为16字节一页, AT24C32/64为32字节为一页, AT24C128/256为64字节为一页。我们完全可以通过写页数据,通过判断是否有数据被覆盖实现,过程如下:
写入16字节数据->读写一致为AT24C02/04/08/16中一种,否则判断8字节是否一致,一致为AT24C01,否则为其他->通过写最大地址判断是否覆盖,来判断究竟是AT24C02/04/08/16中的那一致。其他型号过程一致,代码如下:
- ATC_TYP ATCReadType(void)
- {
- u8 i;
- u8 tmpBuf[64] = {0};
- u8 tmpDat[64] = {0};
- u8 cmpDat[64] = {0};
- //--------------------------------- 单地址判断 -----------------------------
- for (i=0; i<16; i++) // 初始化
- {
- tmpDat[i] = i;
- cmpDat[i] = i;
- }
- // AT24C01的页为8字节,AT24C02/04/08/16的页为16字节
- // 通过读写16来判断页大小,从而区分AT24C01
- ATCReadNByte(AT24C02, 0, tmpBuf, 16); // 数据暂存
- ATCWriteNByte(AT24C02, 0, tmpDat, 16); // 写入验证数据
- memset(tmpDat, 0, 16);
- ATCReadNByte(AT24C02, 0, tmpDat, 16); // 读验证数据
- if (memcmp(tmpDat, cmpDat, 16) == 0) // AT24C02/04/08/16
- {
- ATCWriteNByte(AT24C02, 0, tmpBuf, 16); // 恢复数据
- // AT24C02/04/08/16中,通过页地址共同组成地址,故可以通过页区别型号
- for (i=4; i>0; i--)
- {
- ATCReadByte((ATC_TYP)(i), ATC_Par[(ATC_TYP)(i)].MaxAddr, &tmpDat[0]);
- ATCWriteByte((ATC_TYP)(i), ATC_Par[(ATC_TYP)(i)].MaxAddr, 0xAA);
- ATCReadByte((ATC_TYP)(i), ATC_Par[(ATC_TYP)(i)].MaxAddr, &tmpDat[1]);
- if (tmpDat[1] == 0xAA)
- {
- ATCWriteByte((ATC_TYP)(i), ATC_Par[(ATC_TYP)(i)].MaxAddr, tmpDat[0]);
- return ((ATC_TYP)(i));
- }
- }
- }
- else
- {
- if (memcmp(&tmpDat[8], cmpDat, 8) == 0) // AT24C01
- {
- ATCWriteNByte(AT24C01, 0, tmpBuf, 8); // 恢复数据
- return AT24C01;
- }
- }
- //--------------------------------- 双地址判断 -----------------------------
- for (i=0; i<64; i++) // 初始化
- {
- tmpDat[i] = i;
- cmpDat[i] = i;
- }
- ATCReadNByte(AT24C128, 0, tmpBuf, 64); // 数据暂存
- ATCWriteNByte(AT24C128, 0, tmpDat, 64); // 写入验证数据
- memset(tmpDat, 0, 64);
- ATCReadNByte(AT24C128, 0, tmpDat, 64); // 读验证数据
- if (memcmp(tmpDat, cmpDat, 64) == 0) // AT24C128/256
- {
- ATCWriteNByte(AT24C128, 0, tmpBuf, 64); // 恢复数据
- ATCReadByte(AT24C256, 0, &tmpDat[0]);
- ATCReadByte(AT24C256, ATC_Par[AT24C128].Capacity, &tmpDat[1]);
- ATCWriteByte(AT24C256, 0, 0xAA);
- ATCWriteByte(AT24C256, ATC_Par[AT24C128].Capacity, 0x55);
- ATCReadByte(AT24C256, 0, &tmpDat[2]);
- ATCReadByte(AT24C256, ATC_Par[AT24C128].Capacity, &tmpDat[3]);
- if ((tmpDat[2] == 0xAA) && (tmpDat[3] == 0x55))
- {
- ATCWriteByte(AT24C256, 0, tmpDat[0]);
- ATCWriteByte(AT24C256, ATC_Par[AT24C128].Capacity, tmpDat[1]);
- return AT24C256;
- }
- else
- {
- ATCWriteByte(AT24C128, 0, tmpDat[0]);
- return AT24C128;
- }
- }
- else // AT24C128/256
- {
- if (memcmp(&tmpDat[32], cmpDat, 32) == 0)
- {
- ATCWriteNByte(AT24C64, 0, tmpBuf, 32);
- ATCReadByte(AT24C64, 0, &tmpDat[0]);
- ATCReadByte(AT24C64, ATC_Par[AT24C32].Capacity, &tmpDat[1]);
- ATCWriteByte(AT24C64, 0, 0xAA);
- ATCWriteByte(AT24C64, ATC_Par[AT24C32].Capacity, 0x55);
- ATCReadByte(AT24C64, 0, &tmpDat[2]);
- ATCReadByte(AT24C64, ATC_Par[AT24C32].Capacity, &tmpDat[3]);
- if ((tmpDat[2] == 0xAA) && (tmpDat[3] == 0x55))
- {
- ATCWriteByte(AT24C64, 0, tmpDat[0]);
- ATCWriteByte(AT24C64, ATC_Par[AT24C32].Capacity, tmpDat[1]);
- return AT24C64;
- }
- else
- {
- ATCWriteByte(AT24C32, 0, tmpDat[0]);
- return AT24C32;
- }
- }
- }
- return ATC_TYP_MAX;
- }
我们知道从AT24C01~256之间,由于容量和地址的区别,我们可以把这一系列分为三大类:
1. 单地址,直接8位地址操作:AT24C01/02
2. 单地址,8位地址加3位页地址组合操作:AT24C04/08/16
3. 双地址,直接16位地址操作:AT24C32/64/128/256
由上面的三大类我们可以看出,要区别型号,可以通过先分类,再分别通过每一类中型号的差异进行进一步的区别。
首先我们可以看出,从地址上来分可以把以上芯片分为单地址和双地址,从页面组合上来分我们可以把他们分为有组合和无组合两种,所以,我们可以通过这两种方法先把这系列芯片分为2类,之后再进行细分。例如:
从地址上来分->单地址为AT24C01/02/04/08/16,双地址为AT24C32/64/128/256:
1. 单地址5种芯片,有页组合的有3种,这三种中 AT24C16由3位组合, AT24C08由2位组合, AT24C04由一位组合,所以当我们读写0xAE地址正确时一定是AT24C16,如果不正确,读写0xA6正确时一定是 AT24C08,如果还不正确,读写0xA2正确时一定是 AT24C04,如果还不正确那,一定是 AT24C01/02中的一种,而对于 AT24C01/02来说,不同的只是地址范围,如果写地址0和地址128,如果数据覆盖,那一定是 AT24C01,如果没有覆盖那一定是 AT24C02.
2. 双地址4种芯片,没有也组合,而不同的只有地址范围,这一定和 AT24C01/02是完全一致的,所以,由于区别的方法也和这两个芯片一样,通过覆盖可以轻松的判断出芯片型号。
- /**************************************************************************************
- * FunctionName : ATCReadType()
- * Description : 写器件型号
- * EntryParameter : None
- * ReturnValue : None
- **************************************************************************************/
- u8 ATCReadType(void)
- {
- u8 i;
- u8 tmpBuf[3] = {0};
- u8 tmpDat[3] = {0};
- ATCReadNByte(AT24C32, 0, tmpBuf, 1); // 读取双地址0的一字节暂存
- ATCReadNByte(AT24C16, 0, &tmpBuf[1], 2); // 读取单地址0的二字节暂存
- ATCWriteNByte(AT24C32, 0, "\xA5", 1); // 按照双地址格式写入一字节数据
- ATCReadNByte(AT24C32, 0, tmpDat, 1); // 按照双地址格式读取一字节数据
- ATCReadNByte(AT24C16, 0, &tmpDat[1], 2); // 按照单地址格式读取二字节数据
- if ((tmpDat[1] == 0x00) && (tmpDat[2] == 0xA5)) // 单地址芯片
- {
- ATCWriteNByte(AT24C16, 0, &tmpBuf[1], 2); // 恢复数据
- //-------------------------------- AT24c04/08/16 -----------------------
- for (i=AT24C16; i>AT24C02; i--) // AT24c04/08/16
- {
- ATCReadByte(i, ATC_Par[i].MaxAddr, &tmpBuf[0]);
- ATCWriteByte(i, ATC_Par[i].MaxAddr, 0xAA);
- ATCReadByte(i, ATC_Par[i].MaxAddr, &tmpDat[0]);
- ATCWriteByte(i, ATC_Par[i].MaxAddr, tmpBuf[0]);
- if (tmpDat[0] == 0xAA)
- {
- return i;
- }
- }
- //-------------------------------- AT24c01/02 --------------------------
- ATCReadByte(AT24C02, 0, &tmpBuf[0]);
- ATCReadByte(AT24C02, 128, &tmpBuf[1]);
- ATCWriteByte(AT24C02, 0, 0xAA);
- ATCWriteByte(AT24C02, 128, 0x55);
- ATCReadByte(AT24C02, 0, &tmpDat[0]);
- ATCReadByte(AT24C02, 128, &tmpDat[1]);
- ATCWriteByte(AT24C02, 0, tmpBuf[0]);
- ATCWriteByte(AT24C02, 128, tmpBuf[1]);
- return (tmpDat[0] == 0x55) ? AT24C01 : AT24C02;
- }
- else
- {
- if (tmpDat[0] == 0xA5) // 双地址芯片
- {
- ATCWriteNByte(AT24C256, 0, &tmpBuf[0], 1); // 恢复数据
- //-------------------------------- AT24c32/64/128/256 --------------
- for (i=AT24C256; i>AT24C16; i--)
- {
- ATCReadByte(i, 0, &tmpBuf[0]);
- ATCReadByte(i, ATC_Par[i-1].Capacity, &tmpBuf[1]);
- ATCWriteByte(i, 0, 0xAA);
- ATCWriteByte(i, ATC_Par[i-1].Capacity, 0x55);
- ATCReadByte(i, 0, &tmpDat[0]);
- ATCReadByte(i, ATC_Par[i-1].Capacity, &tmpDat[1]);
- ATCWriteByte(i, 0, tmpBuf[0]);
- ATCWriteByte(i, ATC_Par[i-1].Capacity, tmpBuf[1]);
- if ((tmpDat[0] == 0xAA) && (tmpDat[1] == 0x55))
- {
- return i;
- }
- }
- return AT24C256;
- }
- else // 非AT系列芯片
- {
- return ATC_TYP_MAX;
- }
- }
- }
在单地址和双地址的判断中,我们按照两种方法进行数据暂存,因为我们刚开始并不知道卡片究竟是什么型号,所以,在判断出来后,按照单双地址分别恢复也保证数据的正确性。
还有一点需要强调,我们在按照双地址方式,在地址0的地方写入0xA5数据,如果芯片确实是双地址,那么该数据一定写入地址0,数据数据为0xA5,但是如果该芯片为单地址,那么地址的低8为就被作为数据一起写入了,所以会导致,地址0开始写入两字节数据,一字节为0(地址的低8位被当做第一个数据了),一字节为0xA5(数据却作为第2字节数据写入了)。所以通过这两字节数据可以轻松的判断出究竟是单地址还是双地址。
上一篇:单片机之串口数据处理
下一篇:IAR开发STM8字体异常问题解决
推荐阅读最新更新时间:2024-03-16 15:00