小白菜的EEPROM学习之路 AT24Cxx全系列驱动编写 零:接上回书 上回书说到了标准IIC的编写,函数参数是指针和字节数,并非是以IIC地址和寄存器地址作参数,何故?小白菜考虑着,要想真正的适应大部分IIC器件的IIC操作而不必写两个功能相同的函数,用指针和字节数作参数是最好的选择。大虾们可能觉得这是为了统一而统一,这点必须承认。在参数传递效率上,确实比直接传递数据要低,但是小白菜接触到的IIC器件,没有一个是需要时时写入的(这里的时时是指MCU空闲下来就读取或写入IIC器件数据)。用的最多的是EEPROM、还有xxx7290(键盘数码管扫描芯片)、铁电存储器,测温芯片。这些器件没有一个是需要实时操作的,也就是说小白菜接触的IIC器件对实时性要求不高。于是,小白菜可以忽略参数传递效率的问题。当然“为了统一而统一”这个目的是去不掉的。 一:为了那“可爱”的目的。 还是2011年,小白菜空闲时,觉得以前用的AT24Cxx系列EEPROM的驱动不能相互兼容,你用24C02和用24C64,就得换驱动,从工程中去掉原来,然后添加上新的,相当的麻烦。于是小白菜便想,都是同一系列的产器,为什么不能用同一个驱动? 于是,小白菜得到了他的目的: 1编写一个能适应AT24Cxx全系列的驱动函数,对外只有两个函数,写EEPROM函数和读EEPROM函数; 2移植时(包括更换不同容量的芯片)只需要改变很少的宏定义选项就能完成。 二:发现共性(操作过程的比较与抽象) 1为了实现这两个目的,小白菜开始看手册,并写记录下AT24Cxx系列EEPROM的一些参数,见表1和表2。 表2:AT24Cxx的IIC操作地址比较(二进制) A2、A1、A0指的是芯片引脚,a14—a0指的是字节地址 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 0 0 a13 a12 a11 a10 a9 a8 | | | | 0 a14 a13 a12 a11 a10 a9 a8 | |
由表1和表2,小白菜开始想,不同容量的需要进行地址处理和页处理。小白菜还设想,在应用中,不大可能不同容量的EEPROM一起使用;有可能访问的数量大于芯片容量,所以要有溢出检测……小白菜想了很多,并整理了一个大体的思路。 读写函数 → 先要进行参数检查 → 进行溢出检测 → 地址处理 → 读写数据( → 写数据时写入等待) → 返回操作状态。 三 代码编写 小白菜用上面的流程,开始了代码的编写。写代码时,地址处理部分需要使用条件编译来实现不同芯片的操作;写入等待函数需要有超时机制……写啊写,写啊写,小白菜终于写出来了。列位看官,请继续向下看。 四 使用说明 4.1 移植修改 移植修改在H文件中的“移植修改”部分。这里有5处需要修改。 //----------------------------------------------------------------------------// // 编号:1 // 名称: // 功能:单片机寄存器头文件,例如reg51.h //----------------------------------------------------------------------------// #include "ATT703x.H" 4.1.1 请您把使用的单片机的头文件包含进来。大虾们用过MCU 多了,知道不同的MCU,其寄存器定义是不一样滴,不是所有的51 单片机都用Reg51.H 或Reg52.H 头文件。 //--------------------------------------------------------------------------// // 编号 :2 // 名称 :AT24Cxx // 功能 :选择您所使用的EEPROM芯片型号。只能启用一条宏。 // :不支持一条总线上挂不同的EEPROM,支持同类型的多个EEPROM挂在总线上。 //--------------------------------------------------------------------------// // #defineAT24C01 // 使用AT24C01,则启用本句并屏蔽其它语句。 // #defineAT24C02 // 使用AT24C02,则启用本句并屏蔽其它语句。 #defineAT24C04 // 使用AT24C04,则启用本句并屏蔽其它语句。 // #defineAT24C08 // 使用AT24C08,则启用本句并屏蔽其它语句。 // #defineAT24C16 // 使用AT24C16,则启用本句并屏蔽其它语句。 // #defineAT24C32 // 使用AT24C32,则启用本句并屏蔽其它语句。 // #defineAT24C64 // 使用AT24C64,则启用本句并屏蔽其它语句。 // #defineAT24C128 //使用AT24C128,则启用本句并屏蔽其它语句。 // #defineAT24C256 //使用AT24C256,则启用本句并屏蔽其它语句。 4.1.2 这里启用您所用的芯片。不支持不同容量的芯片挂接在同一总线上。 //--------------------------------------------------------------------------// // 编号 :3 // 名称 :AT24CXX_WP_ENABLE // 功能 :启用AT24Cxx的写保护功能。为1时启用写保护。为0时不使用写保护。 // :当WP引脚接地时,请禁用写保护功能。否则会浪费系统资源。 //--------------------------------------------------------------------------// #define AT24CXX_WP_ENABLE (0) //--------------------------------------------------------------------------// // 编号 :4 // 名称 :AT24Cxx_WP // 功能 :写保护引脚所用的口线。启用写保护时,才需要设置本参数 //--------------------------------------------------------------------------// #if (1 == AT24CXX_WP_ENABLE) ***it AT24Cxx_WP = P1^2; #endif 4.1.3 这里是关于WP的操作,您可能并不使用写保护并把WP接地。如果AT24CXX_WP_ENABLE为0,即不使用写保护时,写入允许和禁止函数不编译。如果您为了减少改动,也可以把这两个函数体进行条件编译,而只留下一个“空函数”。 //--------------------------------------------------------------------------// // 编号 :5 // 名称 :AT24Cxx_Delay_1ms() // 功能 :精确的1毫秒延时函数。这里请使用您系统中的微秒延时函数。 // :例如,您的延时函数是Delay_1us(),那么您可以使用下句 // :#defineMK_Delay_1us() Delay_1us() // :来实现延时。 //--------------------------------------------------------------------------// #include "Delay.H" // 您系统所用延时函数声明所在的头文件。 #define AT24Cxx_Delay_1ms() Delay_MS(1) 4.1.4 这里的软件1ms延时函数用于写入等待。延时必须在1ms左右。 4.2 函数说明 4.2.1 从AT24Cxx中读取数据函数 //----------------------------------------------------------------------------// // 从AT24Cxx中读取多字节数据函数(对外接口) //函数名称:AT24Cxx_Read_Str //函数功能:从Addr指定的地址开始读取AT24Cxx,一共读取Num个字节,数据读出后存 // 放在PDat数组中。 //入口参数: // A2A1A0:对应芯片A2 A1 A0引脚,低3位有效(高位被忽略). // Addr:对24Cxx进行读操作的起始地址。 // *PDat:数据读取后存放的首地址 // Num :要读取的字节数 //出口参数:0 = 成功,1 = 失败。 //重要说明:1.读取的第一个字节放在PDat[0]中,第二个放在PDat[1]中,以此类推。 // :2.若EEPROM剩余空间不足,函数报错。 //----------------------------------------------------------------------------// extern uint8 AT24Cxx_Read_Str(uint8 A2A1A0, uint16 Addr, uint8*PDat, uint16 Num) 第一个参数A2A1A0的低三位分别对应A2、A1、A0,并且不能对该参数进行检查,所以一定要设置正确。 应用示例: 芯片的A2、A1、A0接地,并且从0x10地址开始读取,读取的字节数50,数据读取后存放在unsigned char Buf[100]数组中。 解析:由于A2、A1、A0接地,所以第一个参数为0,函数调用是 AT24Cxx_Read_Str(0, 0x10, Buf, 50) Buf中存放的EEPROM中(0x10 + i)单元的内容, 4.2.2 向AT24Cxx中写入数据函数 //-------------------------------------------------------------------------------// // 向AT24Cxx写入多字节数据函数(对外接口) //函数名称:AT24Cxx_Write_Str //函数功能:向AT24Cxx中写入多字节。写入的起始地址由Addr确定,数据存放的首 // 地址在PDat中存放,写入的字节数是Num(16位无符号数)。 //入口参数: // A2A1A0:对应芯片A2 A1 A0引脚,低3位有效(高位被忽略). // Addr:对24Cxx进行写操作的起始地址。 // *PDat:发送的数据存放的首地址 // Num :发送的字节数 //出口参数:0 = 成功,1 = 失败。 //重要说明:1.先发送PDat[0],再发送PDat[1],以此类推。 // :2.若EEPROM剩余空间不足,函数报错。 // :3.若启用了写保护功能,必须先使写保护失效,否则无法进行写入。 //-------------------------------------------------------------------------------// extern uint8 AT24Cxx_Write_Str(uint8 A2A1A0, uint16 Addr, uint8*PDat, uint16 Num) 第一个参数A2A1A0的说明见4.2.1。 若启用了写保护功能,在调用本函数必须调用写入允许函数,否则函数写入出错。 应用示例: 芯片的A2、A1、A0接地,并且从0x10地址开始写入,写入的字节数50,写入数据存放在unsigned char Buf[100]数组中。Buf写入EEPROM中(0x10 + i)单元。 解析:A2、A1、A0接地,所以第一个参数为0,函数调用是 AT24Cxx_ Write _Str(0, 0x10, Buf, 50) 4.2.3 AT24Cxx定义允许禁止函数 extern voidAT24Cxx_Write_Enable(void); // 允许写入。 extern voidAT24Cxx_Write_Disable(void); // 禁止写入。 这两个函数是写入允许和禁止函数,实际是操作WP引脚。您也可以改为宏定义,这里小白菜就不弄啦。这两个函数受AT24CXX_WP_ENABLE的控制。AT24CXX_WP_ENABLE为1时,即打开写保护,当写入时,必须先调用AT24Cxx_Write_Enable()函数,以使能写入。 这对函数应成对的调用哦(就像进入和退出临界区函数一样),要不然写保护有没有意义了。 五 最后的有用的话 这套驱动,小白菜只测试过AT24C64和AT24C04,其他并没有测试过。所以要慎用哦。欢迎各位童鞋进行拍砖!要是有Bug,小白菜也非常希望大家能给小白菜说一声哦~ 非常感谢~ 3htech 我是一颗小白菜
|