打印

小白菜的EEPROM学习之路

[复制链接]
4266|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
3htech|  楼主 | 2012-8-26 11:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 3htech 于 2012-8-26 11:40 编辑

小白菜的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
  
1AT24Cxx比较

  
  
型号

  
  
容量(字节)

  
  
最大级联数

  
  
页字节数

  
  
地址字节数

  
  
写入时间

  
  
AT24C01

  
  
128 = 0x0080

  
  
8

  
  
8

  
  
2

  
  

  
5ms

  
(最大)

  
  
AT24C02

  
  
256 = 0x0100

  
  
8

  
  
8

  
  
2

  
  
AT24C04

  
  
512 = 0x0200

  
  
4

  
  
16

  
  
2

  
  
AT24C08

  
  
1024 = 0x0400

  
  
2

  
  
16

  
  
2

  
  
AT24C16

  
  
2048 = 0x0800

  
  
1

  
  
16

  
  
2

  
  
AT24C32

  
  
4096 = 0x1000

  
  
8

  
  
32

  
  
3

  
  
AT24C64

  
  
8192 = 0x2000

  
  
8

  
  
32

  
  
3

  
  
AT24C128

  
  
16384 = 0x4000

  
  
8

  
  
64

  
  
3

  
  
AT24C256

  
  
32768 = 0x8000

  
  
8

  
  
64

  
  
3

  




  
2AT24CxxIIC操作地址比较(二进制)

  
A2A1A0指的是芯片引脚,a14—a0指的是字节地址

  
  
型号

  
  
1个字节

  
MSB----LSB

  
  
2个字节

  
MSB----LSB

  
  
3字节

  
MSB----LSB

  
  
AT24C01

  
  1     0     1    0
  A2   A1   A0    R/W
  
  0   a6   a5   a4
  a3  a2   a1   a0
  
  不发送
  
  
AT24C02

  
  1     0     1    0
  A2   A1   A0    R/W
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  不发送
  
  
AT24C04

  
  1     0     1    0
  A2   A1   a8   R/W
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  不发送
  
  
AT24C08

  
  1     0     1    0
  A2   a9    a8    R/W
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  不发送
  
  
AT24C16

  
  1     0     1    0
  a10  a9    a8    R/W
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  不发送
  
  
AT24C32

  
  1     0     1    0
  A2   A1   A0    R/W
  
   0    0    0    0
  a11  a10   a9   a8
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  
AT24C64

  
  1     0     1    0
  A2   A1   A0    R/W
  
  0    0     0     a12
  a11  a10   a9   a8
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  
AT24C128

  
  1     0     1    0
  A2   A1   A0    R/W
  
  0    0    a13    a12
  a11  a10   a9   a8
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  
  
AT24C256

  
  1     0     1    0
  A2   A1   A0    R/W
  
  0    a14   a13   a12
  a11  a10   a9   a8
  
  a7  a6   a5   a4
  a3  a2   a1   a0
  


由表1和表2,小白菜开始想,不同容量的需要进行地址处理和页处理。小白菜还设想,在应用中,不大可能不同容量的EEPROM一起使用;有可能访问的数量大于芯片容量,所以要有溢出检测……小白菜想了很多,并整理了一个大体的思路。
读写函数先要进行参数检查 进行溢出检测 → 地址处理 → 读写数据( →
写数据时写入等待) → 返回操作状态。

三 代码编写
小白菜用上面的流程,开始了代码的编写。写代码时,地址处理部分需要使用条件编译来实现不同芯片的操作;写入等待函数需要有超时机制……写啊写,写啊写,小白菜终于写出来了。列位看官,请继续向下看。

AT24Cxx.rar

194.63 KB

相关帖子

沙发
3htech|  楼主 | 2012-8-26 11:41 | 只看该作者
四 使用说明
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)
    sbit   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的低三位分别对应A2A1A0,并且不能对该参数进行检查,所以一定要设置正确。
应用示例:

芯片的A2A1A0接地,并且从0x10地址开始读取,读取的字节数50,数据读取后存放在unsigned char Buf[100]数组中。
解析:由于A2A1A0接地,所以第一个参数为0,函数调用是
AT24Cxx_Read_Str(0, 0x10,  Buf,  50)
Buf中存放的EEPROM(0x10 + i)单元的内容,
4.2.2 AT24Cxx中写入数据函数
//-------------------------------------------------------------------------------//
//                    AT24Cxx写入多字节数据函数(对外接口)

//函数名称:AT24Cxx_Write_Str
//函数功能:向AT24Cxx中写入多字节。写入的起始地址由Addr确定,数据存放的首
//         地址在PDat中存放,写入的字节数是Num16位无符号数)。
//入口参数:
//         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
若启用了写保护功能,在调用本函数必须调用写入允许函数,否则函数写入出错。
应用示例:

芯片的A2A1A0接地,并且从0x10地址开始写入,写入的字节数50,写入数据存放在unsigned char Buf[100]数组中。Buf写入EEPROM(0x10 + i)单元。
解析:A2A1A0接地,所以第一个参数为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
我是一颗小白菜

使用特权

评论回复
板凳
xdzhuifeng| | 2012-8-26 13:51 | 只看该作者
这是干什么的,:sleepy:

使用特权

评论回复
地板
jialio| | 2012-8-26 21:27 | 只看该作者
不怎么看懂啊 慢慢看MRO

使用特权

评论回复
5
zxcscm| | 2012-8-27 19:40 | 只看该作者
收藏了

使用特权

评论回复
6
outstanding| | 2013-7-18 11:36 | 只看该作者
不错啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:我是一颗小白菜~!

20

主题

416

帖子

3

粉丝