eeprom驅動的移植

轉載至:https://blog.csdn.net/ZhengNice/article/details/50070435

這篇文章主要是總結一下eeprom的驅動製作以及測試程序的編寫。

開發環境

開發環境:Centos6.5

內核版本:Linux3.0

交叉編譯器版本: buildroot-2012.08

以下爲舊內核,新版內核需要修改設備樹。

原理圖部分

img
img

上面的圖中,左圖是eeprom的底板原理圖,從圖中我們得知,此開發板的eeprom使用的是AT2402的芯片,結合右圖可以看出eeprom的時鐘和數據信號與開發板的i2c總線的scl和sda接口連接在一起,說明此eeprom是掛載在i2c總線下。

移植

開發板內核配置

首先啓用i2c

Generic Driver Options  --->  

	<*> I2C support  --->   

img
然後啓用eeprom

[*] Misc devices  --->              
	EEPROM support  ---> The new 

img

添加硬件信息

在內核源程序中i2c總線eeprom驅動程序添加

修改linux/arch/arm/mach-s3c2440/mach-smdk2440.c文件

在其中添加

#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顯示如下

img

到這裏我們就完成了Linux內核中i2c總線下eeprom的驅動製作。

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 = -1int 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=-1int 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() ----- */  

測試結果:

img

總線驅動操作

下面是通過操作總線驅動設備來操作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);
}

思考

問題1

I2C設備驅動的設備節點在哪?


驅動編譯爲模塊:.ko文件。

加載驅動模塊:`insmod at24.ko`

加載驅動之後,通過find找到接口:`find / -name "at24"` 、`find / -name "eeprom"`

sys api接口:`/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom`

問題2

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定好的標準。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章