I2C驅動分析與應用程序編寫

I2C驅動分析:

http://www.arm9home.net/read.php?tid-18473-fpage-0-toread--page-1.html

 

 

 

 

http://www.arm9home.net/read.php?tid-10768-fpage-0-toread--page-1.html
Mini2440之i2c驅動的應用程序祥解【希望對都是初學者的我們都有用】

 
圖片:
圖片:
Mini2440之i2c驅動(1)

首先,要明白的是,mini2440-128M上面用的eeprom的型號爲AT24C08B,下載手冊可知這個eeprom的大小爲1024*8(8k),這是什麼意思呢?先補充一下存儲器相關方面的知識,存儲器的容量是以存儲一位二進制數(bit)爲單位的,因此,存儲器的容量即指每個存儲器芯片所能存儲的二進制數的位數,因此,在標定存儲器容量時,同時標出存儲器存儲單元的數目和位數,即:
    存儲器芯片容量=存儲單元數*數據位數
用AT24C08B來具體解釋就是,AT24C08B共有1024個存儲單元,每個存儲單元的大小事8位(bits),而我們知道8位爲一個字節,所以mini2440開發板的eeprom的大小爲1024個字節,即1K大小,那麼括弧裏面的8K是什麼意思的,8K的意思是總共有8*1024個位,不要混淆了哦。所以mini2440用戶手冊上說eeprom的大小是256字節是錯誤的,256字節那是AT24C0A的大小。
下面來看下AT24C08B地址的確定:
看手冊知:

從上面知,一條I2C總線上最多隻能存在兩個8K的eeprom,他們用A2這根線來區別。
而A0,A1對於AT24C08B是用不到的,要麼不連,要麼全接地,看下mini2440的原理圖:

可以看到A2接到了地,而A0,A1選擇了接到地,而不是不連。順便提一句,wp接地表示可以正常讀寫,沒有進行寫保護,可以自己看手冊,很容易的。那麼總麼確定AT24C08B的地址,接着看手冊:



在看下寫時序:

看見了,device address 的地址是不包含R/W的,看對應關係,呵呵,所以地址爲1010000,即0x50,而eeprom裏面的範圍通過寫時序圖也可以看出只能是0x00000000---0xFFFFFFFF,這個從後面的程序測試中可以看出在這個範圍以外的是沒法讀寫的。

好,下面開始寫驅動程序,這裏可以參考下劉洪濤老師的實例解析linux內核I2C體系結構(1),本篇先利用I2C_DEV來實現,在內核層的i2c設備驅動程序等我弄出來再寫哈。
這裏利用ioctl()方法,不用read(),write方法。因爲AT24C08B的讀時序中要有重複開始信號。從手冊可以看出,如下圖:

好了,利用i2c-dev.c操縱適配器,進而操作i2c設備的旅程開始了:
首先熟悉:struct i2c_rdwr_ioctl_data,struct i2c_msg結構體:



上面的解釋很清楚,我就不解釋了,接下來,開始寫程序,在寫程序之前,首先要保證內核包括對s3c2410適配器的支持,即你下到開發板上的zImage,不是用make zImage生成的嘛,那麼在make zImage不是要make menuconfig嗎,在這裏面的devices driver support 中要將s3c2410適配器驅動選成*,編譯進內核,可以看開發板有沒有/dev/i2c/x這個目錄,有就沒問題了。
程序如下,附解釋:
#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

int main(int argc, char **argv)
{
    struct i2c_rdwr_ioctl_data e2prom_data;
    unsigned int fd;
    unsigned int slave_address, reg_address,value; //slave_address爲eeprom的地址,reg_address爲eeprom中存儲單元的地址,範圍0x0--0xFFFFFFFF,value爲你要寫進eeprom的值
    int ret;
    
    if (argc < 5){
        printf("Usage:\n%s /dev/i2c/x start_addr reg_addr value\n",argv[0]);
        return 0;
    }
    
    fd = open(argv[1], O_RDWR);
    //如果flag參數裏有O_CREAT表示,該文件如果不存在,系統則會創建該文件,該文件的權限由第三個參數決定,此處爲0755
//如果flah參數裏沒有O_CREAT參數,則第三個參數不起作用.此時,如果要打開的文件不存在,則會報錯.
//所以fd=open(argv[1],O_RDWR),僅僅只是打開指定文件


    if (!fd){
        printf("Error on opening the device file\n");
        return 0;
    }

    sscanf(argv[2], "%x", &slave_address);
    sscanf(argv[3], "%x", ®_address);
    sscanf(argv[4], "%x", &value);
    
    e2prom_data .nmsgs = 2;//因爲都時序要兩次,所以設爲2
    e2prom_data .msgs = (struct i2c_msg *)malloc(e2prom_data.nmsgs * sizeof(struct i2c_msg));

void *malloc(int size); 
  說明:malloc 向系統申請分配指定size個字節的內存空間。返回類型是 void* 類型。void* 表示未確定類型的指針。C,C++規定,void* 類型可以強制轉換爲任何其它類型的指針。

    if (!e2prom_data.msgs){
        printf("Memory alloc error\n");
        close(fd);
        return 0;
    }
    
    ioctl(fd, I2C_TIMEOUT, 2);//設置超時時間
    ioctl(fd, I2C_RETRIES, 1);//設置重發次數

    /* write data to e2prom*/
    e2prom_data.nmsgs = 1;  //寫只有進行一次的起動總線、就是說只發送I2C消息的次數只有一次、相反讀的話我們看時序可以發現我們進行了兩次的起動總線、所以我們要發兩次的I2C消息的次數。
    e2prom_data.msgs[0].len = 2;//信息長度爲2,看寫時序,eeprom的地址不算的,因爲付給了addr,而len是指buf中的值的個數
BUF中值的個數要是寫時序的話就是兩個、包括寫入的單元地址和要寫入的信息  分別對應下面的BUF【0】和BUF【1】
    e2prom_data.msgs[0].addr = slave_address;
    e2prom_data.msgs[0].flags = 0;//寫命令
    e2prom_data.msgs[0].buf = (unsigned char*)malloc(2);
    e2prom_data.msgs[0].buf[0] = reg_address;//信息值1 eeprom中存儲單元的地址,即你要往哪寫
    e2prom_data.msgs[0].buf[1] = value;//信息值2,即你要寫什麼
    
    ret = ioctl (fd, I2C_RDWR, (unsigned long)&e2prom_data);//好了 ,寫進去吧
    if (ret < 0){
        printf ("ioctl write error\n");
    }

    printf("you have write %02x into e2prom at %02x address\n",value,reg_address);
    
    sleep(1);
    /*read data from e2prom*/
    e2prom_data.nmsgs = 2;//讀時序要兩次過程,要發兩次I2C消息
//寫只有進行一次的起動總線、就是說只發送I2C消息的次數只有一次、相反讀的話我們看時序可以發現我們進行了兩次的起動總線、所以我們要發兩次的I2C消息的次數。

    e2prom_data.msgs[0].len = 1;//信息長度爲1,第一次只寫要讀的eeprom中存儲單元的地址
    e2prom_data.msgs[0].addr = slave_address; //器件地址
    e2prom_data.msgs[0].flags = 0;//寫命令,看讀時序理解
    e2prom_data.msgs[0].buf[0] = reg_address;//要寫入數據的單元地址
        
    e2prom_data.msgs[1].len = 1;
    e2prom_data.msgs[1].addr = slave_address;   //器件地址
    e2prom_data.msgs[1].flags = I2C_M_RD;//讀命令
    e2prom_data.msgs[1].buf = (unsigned char*)malloc(1);
    e2prom_data.msgs[1].buf[0] = 0;//先清空要讀的緩衝區
    ret = ioctl (fd, I2C_RDWR, (unsigned long)&e2prom_data);//好了,讀吧
    if (ret < 0){
        printf ("ioctl read error\n");
    }
    
    printf("read %02x from e2prom address %02x\n",e2prom_data.msgs[1].buf[0], reg_address);
    
    close(fd);
    return 0;    
}



發佈了41 篇原創文章 · 獲贊 36 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章