关于一般的AT24芯片来说,要获取类型并不杂乱,那便是用眼睛看,这个是没有问题的。可是,假如咱们用的是AT24系列的IC卡呢?假如咱们用的是白卡呢?那么怎样来判别这张卡片终究是什么类型的?
关于以上问题,咱们想咱们都有不同的观点,可是,不知道您是否真实尝试过呢?能正确读取AT24C01到AT24C1024之间的各种类型吗?
为了处理这个问题,自己考虑了好几天,以经过多种实验去验证,今日总算获取成果了,不过自己先声明,我这儿只要3中类型的卡(C02, C16, C64),每种类型有2张或以上,验证都是正确的。假如你有其他类型的卡无妨也试试,假如尝试了,请把成果告知自己,鄙人先谢了。
下面咱们先谈谈这类芯片的一些根底知识,得到这些根底知识后,看您能否想出处理这个问题的办法,再看看办法是否和我相同的。
关于24C系列的IC卡来说,其读写操作彻底和24C系列芯片的读写操作一同,一切,下面咱们就以此系列芯片为根底进行介绍。关于这系列芯片的材料特别多并且也十分详细,下面咱们就借《嵌入式实时操作系统Small RTOS51原理及使用》中的第20章串行E2PROM芯片Cat24WCxx驱动程序的内容来描绘。 尽管这一章中讲的芯片不是ATMEL的,可是和这一系列彻底兼容。 1. 参数表 3.png(92.89 KB, 下载次数: 0) 下载附件保存到相册 2013-10-25 07:58 上传
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);
仿制代码
经过以上实验能够发现,读写24C02和24C16现已没有问题,彻底能够争夺的差异这两类芯片。但仍是不能读取24C64之类的芯片。
进一步修正代码:
咱们知道,由于芯片超地址时会呈现掩盖写入,那么咱们能不能把根底可能会呈现掩盖的当地写入不同值了,假如发现掩盖就能够阐明这个类型是错的,假如没有掩盖就说这个类型是对的:
- 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);
仿制代码
经过上面的代码验证取得,现在能够差异出24C64了,但却不能差异出24C02/16等。为什么会这样呢?接连的读写能够差异24C02/16但不能差异24C64,现在尽管能差异24C64了,可其他的反而不行了,能否把这种办法和接连写入多字节组合呢?
- 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);
仿制代码
经过上面的代码修正和实验,咱们现在能够差异这三种类型了,经过我的计算应该是这种办法现已能够读取这一系列的各种类型,不过由于自己手里只要这3种卡片,其他的没有办法实验。
至于为什么这样写能够完结,自己也还没有一个完好的理论依据,咱们无妨一同想想,假如你先想出来,请告知我一下。
下面咱们举例阐明:
用上面的程序,咱们判别类型的次序是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字节数据写入了)。所以经过这两字节数据能够轻松的判别出终究是单地址仍是双地址。