这篇文章主要是总结一下eeprom的驱动制做以及测试程序的编写。linux
开发环境:Centos6.5api
内核版本:Linux3.0bash
交叉编译器版本: buildroot-2012.08函数
如下为旧内核,新版内核须要修改设备树。工具
上面的图中,左图是eeprom的底板原理图,从图中咱们得知,此开发板的eeprom使用的是AT2402的芯片,结合右图能够看出eeprom的时钟和数据信号与开发板的i2c总线的scl和sda接口链接在一块儿,说明此eeprom是挂载在i2c总线下。测试
首先启用i2cui
Generic Driver Options ---> <*> I2C support --->
而后启用eepromspa
[*] Misc devices ---> EEPROM support ---> The new
在内核源程序中i2c总线eeprom驱动程序添加3d
修改linux/arch/arm/mach-s3c2440/mach-smdk2440.c文件code
在其中添加
#include <linux/i2c/at24.h> static struct at24_platform_data at24c02 = { .byte_len = SZ_2K / 8, .page_size = 8, .flags = 0, }; static struct i2c_board_info __initdata smdk2440_i2c_devs[] = { { I2C_BOARD_INFO("24c02", 0x50), .platform_data = &at24c02, }, /* more devices can be added using expansion connectors */ };
另外在smdk2440_machine_init函数中添加
i2c_register_board_info(0, smdk2440_i2c_devs, ARRAY_SIZE(smdk2440_i2c_devs));
完后上面的步骤以后,咱们从新编译内核,烧录到开发板上,咱们进入开发板,进入目录/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/下,咱们能够看到name和eeprom显示以下
到这里咱们就完成了Linux内核中i2c总线下eeprom的驱动制做。
对于i2c设备,咱们有两种常见的操做方式,一种是经过设备节点操做,一种是i2c总线操做。全部的i2c设备,均可以经过i2c总线设备驱动来操做。
这个测试程序的主要功能是可以在eeprom中读出和写入开发板的sn 、mac以及owner信息,程序具体显示要求以下:
程序编译后的名字叫eeprom, 其使用方法为:
向eeprom中写入信息:
$ eeprom -s FL2440-abc $ eeprom -m 00-11-22-33-44-55 $ eeprom -o Michael
从eeprom中读出信息:
$ eeprom -r sn= FL2440-abc mac= 00-11-22-33-44-55 owner= Michael
测试程序以下:
#include <stdio.h> #include <unistd.h> #include <getopt.h> #include <string.h> #include <sys/ioctl.h> #include <stdlib.h> #include <fcntl.h> #include <sys/io.h> #include <errno.h> #define LEN 50 #define SN_OFS 0 #define SN_LEN 50 #define MAC_OFS 50 #define MAC_LEN 50 #define OWN_OFS 100 #define OWN_LEN 50 /****** help information *********/ void help() { printf("This program is used to read or write MAC information to eeprom.\n"); printf("Mandatory arguments to long options are mandatory for short options too:\n"); printf(" -r [read] read the information of board in eeprom;\n"); printf(" -s [serial] write the serial of board to eeprom ;\n"); printf(" -m [mac] write the mac adress of board to eeprom ;\n"); printf(" -o [owner] write the owner of board to eeprom ;\n"); printf(" -h [help] printf the help information.\n"); } /**** the struct of board_information ******/ struct board_info_s { char sn[SN_LEN]; char mac[MAC_LEN]; char owner[OWN_LEN]; }board_info_t; /**** write the board information into eeprom ******/ void write_eeprom_sn(char *a) { int fd = -1; int ret = -1; fd = open("/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom",O_WRONLY); if(fd < 0) { printf("open file eeprom failed;%s\n",strerror(errno)); } strncpy(board_info_t.sn, a, sizeof(board_info_t.sn)); lseek(fd, SN_OFS, SEEK_SET); ret = write(fd, board_info_t.sn, sizeof(board_info_t.sn)); if(ret < 0) { printf("error: %d:%s\n", errno, strerror(errno)); } printf("the sn information of board %s is writen into eeprom!\n", board_info_t.sn); } void write_eeprom_mac(char *b) { int fd = -1; int ret = -1; fd = open("/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom", O_WRONLY); if(fd < 0) { printf("open file eeprom failed;%s\n", strerror(errno)); } strncpy(board_info_t.mac, b, sizeof(board_info_t.mac)); lseek(fd, MAC_OFS, SEEK_SET); ret = write(fd, board_info_t.mac, sizeof(board_info_t.mac)); if(ret < 0) { printf("error: %d:%s\n", errno, strerror(errno)); } printf("the sn information of board %s is writen into eeprom!\n", board_info_t.mac); } void write_eeprom_owner(char *c) { int fd = -1; int ret = -1; fd = open("/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom",O_WRONLY); if(fd < 0) { printf("open file eeprom failed;%s\n", strerror(errno)); } strncpy(board_info_t.owner, c, sizeof(board_info_t.owner)); lseek(fd, OWN_OFS, SEEK_SET); ret = write(fd, board_info_t.owner, sizeof(board_info_t.owner)); if(ret < 0) { printf("error: %d:%s\n", errno, strerror(errno)); } printf("the sn information of board %s is writen into eeprom!\n", board_info_t.owner); } struct board_write { void (*write_sn)(char *); void (*write_mac)(char *); void (*write_owner)(char *); }board_write_cb; /**** read the board_information from eeprom ****/ void board_read() { int fd = -1; int ret = -1; fd = open("/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom", O_RDONLY); if(fd<0) { printf("open file eeprom failed;%s\n", strerror(errno)); } lseek(fd, SN_OFS, SEEK_SET); ret = read(fd, board_info_t.sn, SN_LEN); if(ret < 0) { printf("read from eeprom eerror:%s !\n", strerror(errno)); } printf("the sn of board_information in eeprom is SN=%s !\n", board_info_t.sn); lseek(fd, MAC_OFS, SEEK_SET); ret=read(fd, board_info_t.mac, MAC_LEN); if(ret < 0) { printf("read from eeprom eerror:%s !\n", strerror(errno)); } printf("the sn of board_information in eeprom is MAC=%s !\n", board_info_t.mac); lseek(fd, MAC_OFS, SEEK_SET); ret = read(fd, board_info_t.owner, MAC_LEN); if(ret < 0) { printf("read from eeprom eerror:%s !\n", strerror(errno)); } printf("the sn of board_information in eeprom is OWNER=%s !\n", board_info_t.owner); } /******************************************************************************** * Description: * Input Args: * Output Args: * Return Value: ********************************************************************************/ int main (int argc, char **argv) { int opt=-1; int ret=-1; if(argc < 2) { help(); } if(sizeof(argv[2]) > LEN) { printf("you putin too much characters!\n"); } board_write_cb.write_sn=write_eeprom_sn; board_write_cb.write_mac=write_eeprom_mac; board_write_cb.write_owner=write_eeprom_owner; struct option long_options[] ={ {"read", no_argument, NULL, 'r'}, {"sn", required_argument, NULL, 's'}, {"mac", required_argument, NULL, 'm'}, {"owner", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; while((opt=getopt_long(argc,argv, ":rs:m:o:h", long_options, NULL)) != -1) { switch(opt) { case 'r': board_read(); break; case 's': board_write_cb.write_sn(optarg); break; case 'm': board_write_cb.write_mac(optarg); break; case 'o': board_write_cb.write_owner(optarg); break; case 'h': help(); break; case '?': printf("Invalid character constant !\n"); break; } } return 0; } /* ----- End of main() ----- */
测试结果:
下面是经过操做总线驱动设备来操做at24的。
全部的i2c设备均可以经过操做总线驱动设备来操做,i2ctool就是这样实现的,更高级的操做能够查看i2ctool的源码。
// 写入的结构体 struct i2c_at24_w { unsigned char addr; unsigned char wdata[8]; }; // 读出的结构体 struct i2c_at24_r { unsigned char addr; unsigned char rdata[128]; }; int main() { int fd =open("/dev/i2c-0", O_RDWR); if (fd< 0) { printf("open /dev/i2c-0 failed\n"); goto exit; } struct i2c_msg msg; struct i2c_at24_r rd = {0}; struct i2c_at24_w wd = {0}; struct i2c_rdwr_ioctl_data ioctl_data; struct i2c_msg msgs; // 要写入的消息 ioctl_data.nmsgs= 1; ioctl_data.msgs= &msgs; // 0地址写入8Byte 0x33,AT24C02一次最多能写入8byte for (int i = 0; i < 8;i++) { wd.wdata[i] = 0x33; } wd.addr = 0x00; msgs.addr = 0x50; msgs.flags = 0; msgs.len = sizeof(struct i2c_at24_w); msgs.buf = (unsigned char*)&wd; printf("ioctl write addr 0, return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data)); ioctl_data.nmsgs= 1; ioctl_data.msgs= &msgs; // 写入要读的地址 msgs.addr = 0x50; msgs.flags = 0; msgs.len = sizeof(rd.addr); msgs.buf = (unsigned char*)&rd.addr; printf("ioctl write address, return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data)); // 连续读取128byte msgs.addr = 0x50; msgs.flags |= I2C_M_RD; msgs.len = sizeof(rd.rdata); msgs.buf = (unsigned char*)&rd.rdata[0]; printf("ioctl read, return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data)); close(fd); }
I2C设备驱动的设备节点在哪?
驱动编译为模块:.ko文件。 加载驱动模块:`insmod at24.ko` 加载驱动以后,经过find找到接口:`find / -name "at24"` 、`find / -name "eeprom"` sys api接口:`/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom`
AT24的地址怎么变成0x50了?
数据手册里AT24C02/04/08,他们的地址都是0xA0,而我看网上的例子都是用0x50地址,用扫描工具看到确实有个0x50地址的设备
[root@EmbedSky nfs]# i2cdetect -y -r 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
内核里linux-2.6.30.4/drivers/misc/eeprom/at24.c有相关介绍。
这个50是什么貌似是内核专门为eeprom而分配的。那么问题来了,之后我本身写一个内核没有的I2C设备驱动,我怎么知道该设备的地址变成多少?
/* * However, misconfiguration can lose data. "Set 16-bit memory address" * to a part with 8-bit addressing will overwrite data. Writing with too * big a page size also loses data. And it's not safe to assume that the * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC * uses 0x51, for just one example. */
是0x50 能够在手册中看到 A2 A1 A0都接地
至于为何是0x50 涉及到I2C总线层
发设备地址时,左移了1位发送的
0x50(0x01010000 << 1) 实际设备地址是0xa0 (1010 0000)
i2c设备的地址通常在芯片手册中能够找到(固然还要根据原理图来肯定),不一样的设备有不一样的地址域,这是ieee定好的标准。