這篇文章主要是總結一下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定好的標準。