u-boot移植随笔(8):EEPROM移植及测试

EEPROM说简单也简单,说难自己也捣鼓好几天。
开发板上的EEPROM是AT24C08,大小1024个字节,使用I2C接口。——严重注意,EEPROM是指设备,而I2C是指接口,两者有着很密切的关系,但不可混淆它们,这点必须注意。

u-boot已经在“封装”好了I2C和EEPROM操作函数了,而底层的24x0的I2C函数也已经实现好了。我们所要做的就是开几个宏定义就OK了。
移植之前我在google上百度了几下,但得到的信息相当少,甚至是无用信息。所以关键时刻还得靠手册、靠README文件。
u-boot的“文档”README文件中关于I2C的说明在I2C Support:下面。
开头便是CONFIG_HARD_I2C | CONFIG_SOFT_I2C,就是说,它们两者只取其一而不能两个都定义,在一个ARM中,有硬件的I2C接口是很正常的事,当然,也可以模拟I2C。
其次要定义CONFIG_SYS_I2C_SLAVE(从设备地址?)和EEPROM的设备地址CONFIG_SYS_I2C_EEPROM_ADDR,还有CONFIG_SYS_I2C_EEPROM_ADDR_LEN,这个宏要么为1,要么为2,8位页地址的定义为1——代码中就是这么说的。
还有一个重要的宏,就是CONFIG_DRIVER_S3C24X0_I2C,这个可以在./drivers/i2c/Makefile中找到,如果定义这个宏,就将s3c24x0_i2c加入u-boot。——这也是u-boot的常用手段,与Linux如出一辙。
上面讲那么多,还不如直接上代码来得爽,所定义的宏如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/******LL START*/
/************************************************************
* EEPROM
* for I2C EEPROM at24c08 from APC405.h based on CAT24WC16
************************************************************/
#define CONFIG_HARD_I2C
#define CONFIG_DRIVER_S3C24X0_I2C /* see ./drivers/i2c/Makefile */
#define CONFIG_SYS_I2C_SLAVE 0x7f /* ?? 0x0f is also OK??*/
#define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 /* ?? */
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1 /* 8-bit */
#define CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW 0x07
#define CONFIG_SYS_I2C_SPEED 10000

/* 2408 has 64 pages, each has 16 bytes:2^4 = 16*/
#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 4
/* for 10 ms, see manual */
#define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 10
#define CONFIG_SYS_EEPROM_PAGE_WRITE_ENABLE

/* cmd for EEPROM */
#define CONFIG_CMD_EEPROM
#define CONFIG_CMD_I2C

/*****LL END*/

u-boot的i2c命令我还不知道怎么使用,网上基本没这资料,用得多的是eeprom命令,它的命令格式如下:

1
2
eeprom read addr off cnt
eeprom write addr off cnt

解释:
addr:内存地址
off:EEPROM的偏移
cnt:要读取的数量,16进制的

下面的是对EEPROM进行操作的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
先查看33000000地址数据:
LATE2440> md.b 33000000 8
33000000: 00 00 00 00 00 00 00 00 ........
LATE2440> eeprom read 33000000 0 8 读(eeprom read/write 内存地址 EEPROM偏移位置 数量)

EEPROM @0x50 read: addr 33000000 off 0000 count 8 ... done
LATE2440> md.b 33000000 8 读出的为0
33000000: 00 00 00 00 00 00 00 00 ........
LATE2440> mm.b 33000000 修改内存中的数据
33000000: 00 ? 6c
33000001: 00 ? 61
33000002: 00 ? 74
33000003: 00 ? 65
33000004: 00 ? 20
33000005: 00 ? 6c
33000006: 00 ? 65
33000007: 00 ? 65
33000008: 00 ? q 退出
LATE2440> md.b 33000000 8 检查内存数据是否正确
33000000: 6c 61 74 65 20 6c 65 65 late lee
LATE2440> eeprom write 33000000 0 8 写到eeprom中

EEPROM @0x50 write: addr 33000000 off 0000 count 8 ... done
LATE2440> eeprom read 33000030 0 8 读到另一个地址

EEPROM @0x50 read: addr 33000030 off 0000 count 8 ... done
LATE2440> md.b 33000030 8 查看这个地址
33000030: 6c 61 74 65 20 6c 65 65 late lee

重启后再读到另一个地址
LATE2440> eeprom read 30008000 0 8

EEPROM @0x50 read: addr 30008000 off 0000 count 8 ... done
LATE2440> md.b 30008000 8 查看
30008000: 6c 61 74 65 20 6c 65 65 late lee
LATE2440> mm.b 30008000 修改
30008000: 6c ? 4c
30008001: 61 ? (回车)
30008002: 74 ?
30008003: 65 ?
30008004: 20 ?
30008005: 6c ? 4c
30008006: 65 ?
30008007: 65 ?
30008008: 00 ? q 退出
LATE2440> eeprom write 30008000 0 8 写入eeprom

EEPROM @0x50 write: addr 30008000 off 0000 count 8 ... done
LATE2440> eeprom read 30000000 0 8 读到另一个地址

EEPROM @0x50 read: addr 30000000 off 0000 count 8 ... done
LATE2440> md.b 30000000 8 查看
30000000: 4c 61 74 65 20 4c 65 65 Late Lee
LATE2440>

注:进行eeprom操作出现的地址、数据等等,均为16进制。

在测试过程中的一些疑问:

1、

CONFIG_SYS_I2C_SLAVE:这个宏字面意思是从设备地址,网上有资料显示它是7位的,在u-boot中,如果对这个宏赋值(或定义),则默认为0,——这是不行的!经过山人的测试,这个7位的从设备地址只要不为0就可以!无论是0x7f还是0x01,都可以。或许可以这样理解:I2C器件最多可以有2^7-1个,只要地址不为0,就表示它是有效设备,跟USB那个意思是一样的。——怎么发现有些拗口?山人其实也不是懂的,勿怪。

2、

CONFIG_SYS_I2C_EEPROM_ADDR:这个宏是设备地址(跟上面那个没有任何关系,只是大家都是地址)。datasheet上说这个地址的组成是:

1
1 0 1 0 A2 A1 A0 R/W

开发板中的A2~A0都接低电平,按理说它应该是0xa0或0xa1才对——而且,我见到有些单片机程序代码中也是这样。
但在u-boot中不然,它是0x50,我好意地改为0xa0,结果出错,读不到数据。山人也不知道是怎么回事。
在cmd_eeprom.c这个文件的do_eeprom函数中,无论读写都会在终端中显示

1
EEPROM @0xXX read: addr XXXX off XX count XX ... done

在代码中是这样调用的:

1
printf (fmt, dev_addr, argv[1], addr, off, cnt);

fmt定义:

1
2
const char *const fmt =
"\nEEPROM @0x%lX %s: addr %08lx off %04lx count %ld ... ";

通过代码的分析,这个EEPROM @0xXX中的XX就是由宏CONFIG_SYS_I2C_EEPROM_ADDR传给它的。
上面的疑问目前还不知道是何原因,记录于此,证明山人曾经努力了一下下。