久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

I2C 驅(qū)動(dòng)開發(fā) 文檔

 swhite 2013-10-26

I2C 驅(qū)動(dòng)開發(fā) 實(shí)例

分類: 嵌入式 c 1044人閱讀 評(píng)論(2) 收藏 舉報(bào)

 I2C 驅(qū)動(dòng)開發(fā) 文檔


一,、        開發(fā)背景

開發(fā)環(huán)境:DM355開發(fā)板

內(nèi)核版本:2.6.10

二、        BQ27501驅(qū)動(dòng)開發(fā)的需求

BQ27501是一個(gè)鋰電池管理的芯片,,可以向外提供鋰電池的有關(guān)的信息,。在用戶空間用戶不能直接訪問bq27501的寄存器,所以要為其編寫驅(qū)動(dòng),,該驅(qū)動(dòng)能夠根據(jù)用戶空間的命令,,返回對(duì)應(yīng)的電池信息。

三,、        I2C 驅(qū)動(dòng)的架構(gòu)

bq27501是通過I2C總線與DM355通信的,,故bq27501的驅(qū)動(dòng)實(shí)際上就是bq27501的I2C讀寫的驅(qū)動(dòng),。

Linux內(nèi)核中的 i2c 驅(qū)動(dòng)程序可以分為三個(gè)層次,如下圖所示:



圖1 Linux I2C體系結(jié)構(gòu)

 

1)  i2c 驅(qū)動(dòng)框架

i2c框架主要有i2c.h和 i2c-core.c文件實(shí)現(xiàn),。它們定義驅(qū)動(dòng)中使用的核心數(shù)據(jù)結(jié)構(gòu),,完成 i2c適配器和設(shè)備驅(qū)動(dòng)的注冊(cè),注銷,,并且實(shí)現(xiàn) i2c驅(qū)動(dòng)的algorithm,。 i2c驅(qū)動(dòng)中的algorithm與適配器無關(guān),它屬于上層代碼,,還包括探測(cè)設(shè)備、檢測(cè)設(shè)備地址的上層代碼,。另外,,i2c-dev.c還用于對(duì) i2c設(shè)備節(jié)點(diǎn)的創(chuàng)建,并完成其訪問方法的實(shí)現(xiàn)等,。

2)  i2c 總線驅(qū)動(dòng)

總線驅(qū)動(dòng)的職責(zé),,是為系統(tǒng)中每個(gè)I2C總線增加相應(yīng)的讀寫方法。但是總線驅(qū)動(dòng)本身并不會(huì)進(jìn)行任何的通訊,,它只是存在那里,,等待設(shè)備驅(qū)動(dòng)調(diào)用其函數(shù)。在系統(tǒng)開機(jī)時(shí),,首先裝載的是I2C總線驅(qū)動(dòng),。一個(gè)總線驅(qū)動(dòng)用于支持一條特定的I2C總線的讀寫。這部分主要定義i2c_adapter和i2c_algorithm數(shù)據(jù)結(jié)構(gòu),,前者用來描述具體的 i2c 總線適配器,,后者則描述i2c 總線的通信方法。一般總線驅(qū)動(dòng)由平臺(tái)提供實(shí)現(xiàn),,davinci的i2c總線驅(qū)動(dòng)在i2c-davinci.c文件中定義了,,然后通過i2c_davinci_init函數(shù)調(diào)用i2c_add_adapter(i2c_davinci_adap)將這個(gè)兩個(gè)模塊注冊(cè)到操作系統(tǒng)中,總線驅(qū)動(dòng)就裝上了,。

  1. static struct i2c_adapter i2c_davinci_adap = {  
  2.     .owner = THIS_MODULE,  
  3.     .name = "DAVINCI I2C adapter",  
  4.     .id = I2C_ALGO_EXP,  
  5.     .algo = &i2c_davinci_algo,  
  6.     .algo_data = NULL,  
  7.     .client_register = NULL,  
  8.     .client_unregister = NULL,  
  9. };  
  10. static struct i2c_algorithm i2c_davinci_algo = {  
  11.     .name = "DAVINCI I2C algorithm",  
  12.     .id = I2C_ALGO_EXP,  
  13.     .master_xfer = i2c_davinci_xfer,  
  14.     .smbus_xfer = NULL,  
  15.     .slave_send = NULL,  
  16.     .slave_recv = NULL,  
  17.     .algo_control = NULL,  
  18.     .functionality = i2c_davinci_func,  
  19. };  


3)  i2c 設(shè)備驅(qū)動(dòng)

設(shè)備驅(qū)動(dòng)則是與掛在I2C總線上的具體的設(shè)備通訊的驅(qū)動(dòng),。通過I2C總線驅(qū)動(dòng)提供的函數(shù),設(shè)備驅(qū)動(dòng)可以忽略不同總線控制器的差異,,不考慮其實(shí)現(xiàn)細(xì)節(jié)地與硬件設(shè)備通訊,。它實(shí)現(xiàn)對(duì)具體的i2c設(shè)備的描述,另外還包括一些可能用到的數(shù)據(jù)結(jié)構(gòu),。它借助 i2c框架中的 i2c_probe函數(shù)實(shí)現(xiàn)設(shè)備的attach_adapter方法,,完成設(shè)備檢測(cè)成功后i2c_client 數(shù)據(jù)結(jié)構(gòu)回調(diào)函數(shù)的創(chuàng)建。實(shí)際操作過程中可以跳過i2c_probe函數(shù)直接調(diào)用實(shí)現(xiàn)i2c_client的函數(shù),,這樣可以不必遵循調(diào)用i2c_probe固有的參數(shù)格式,,從而可以提高效率和節(jié)省存儲(chǔ)空間,。bq27501的驅(qū)動(dòng)加載流程如下圖2所示。

 

圖2 bq27501的i2c驅(qū)動(dòng)加載流程圖

 

bq27501_init()函數(shù)為該驅(qū)動(dòng)模塊的模塊初始化函數(shù),,當(dāng)在linux下使用insmod命令裝載該模塊時(shí)會(huì)執(zhí)行該函數(shù),。在初始化函數(shù)中,對(duì)i2c_driver結(jié)構(gòu)體變量初始化,,然后調(diào)用register_chrdev()函數(shù)注冊(cè)字符設(shè)備,。接著調(diào)用i2c_add_driver()函數(shù)添加一個(gè)i2c的driver。i2c_add_driver函數(shù)的執(zhí)行會(huì)引發(fā)i2c_driver中attach_adapter指向的函數(shù)bq27501_i2c_probe_adapter()函數(shù)的執(zhí)行,,該函數(shù)是用來探測(cè)物理設(shè)備的,。它需要通過調(diào)用_i2c_attach_client函數(shù)來實(shí)現(xiàn)探測(cè),并且在_i2c_attach_client函數(shù)內(nèi)調(diào)用i2c_attach_client函數(shù)在總線上附加一個(gè)新的client,;或者調(diào)用i2c-core.c中的i2c_probe函數(shù),,由i2c_probe函數(shù)再調(diào)用探測(cè)物理設(shè)備的函數(shù),bq27501驅(qū)動(dòng)中是采用的第一種方式,。

四,、        實(shí)現(xiàn)I2C驅(qū)動(dòng)編寫方式

最新的內(nèi)核支持兩種編寫i2c驅(qū)動(dòng)的方式,一個(gè)是“Adapter方式(legacy)”,,另一個(gè)是“Probe方式(new style)”,。兩種方式的區(qū)別在于i2c_driver結(jié)構(gòu)體不同,“Adapter方式”的i2c_driver結(jié)構(gòu)是:

  1. struct i2c_driver {  
  2.       structmodule *owner;  
  3.       charname[32];  
  4.       intid;  
  5.       unsignedint class;  
  6.       unsignedint flags;   
  7.       int(*attach_adapter)(struct i2c_adapter*);  
  8.       int(*detach_adapter)(struct i2c_adapter*);  
  9.       int(*detach_client)(struct i2c_client*);  
  10.       int(*command)(struct i2c_client*client,unsigned int cmd, void *arg);  
  11.       structdevice_driver driver;  
  12.       structlist_head list;  
  13. };  


“Probe方式”的i2c_driver結(jié)構(gòu)是:

  1. struct i2c_driver {  
  2.       unsignedint class;  
  3.       int(*attach_adapter)(struct i2c_adapter*);  
  4.       int(*detach_adapter)(struct i2c_adapter*);  
  5.       int (*probe)(struct i2c_client *, const struct i2c_device_id *);  
  6.       int (*remove)(struct i2c_client *);  
  7.       void(*shutdown)(struct i2c_client *);  
  8.       int(*suspend)(struct i2c_client *,pm_message_t mesg);  
  9.       int(*resume)(struct i2c_client *);  
  10.       int(*command)(struct i2c_client*client, unsigned int cmd, void *arg);  
  11.       structdevice_driver driver;  
  12.       const struct i2c_device_id *id_table;  
  13.       int(*detect)(struct i2c_client *,int kind, struct i2c_board_info*);  
  14.       conststruct i2c_client_address_data*address_data;  
  15.       structlist_head clients;  
  16. };  


兩種方式i2c_driver結(jié)構(gòu)主要的不同是后者添加了probe和remove函數(shù)指針和id_table,。bq27501使用的Ti-davinci內(nèi)核是較早版本的,,只支持Adapter方式,不支持Probe方式,,所以bq27501采用的是Adapter方式,。Adapter方式編寫的流程就是上節(jié)中所描述的i2c驅(qū)動(dòng)加載的流程相同。至于Probe方式,,在本次驅(qū)動(dòng)編寫中沒有使用,,所以在此不作詳細(xì)的介紹。兩種方式的對(duì)比可以參加網(wǎng)頁資料http://www./Column/Column213.htm,。

五,、        BQ27501的I2C驅(qū)動(dòng)編寫

要使bq27501的i2c驅(qū)動(dòng)模塊能夠運(yùn)行,必須至少要編寫兩個(gè)文件,,第一個(gè)是驅(qū)動(dòng)的源文件,,第二個(gè)是編譯源文件的Makefile文件。另外該驅(qū)動(dòng)的兩個(gè)關(guān)鍵點(diǎn)是i2c通信和與用戶空間交互數(shù)據(jù),。

1)  Makefile文件編寫

#如果已定義KERNELRELEASE,,則說明是從內(nèi)核構(gòu)造系統(tǒng)調(diào)用的,因此可以利用其內(nèi)建語句,。

ifneq($(KERNELRELEASE),)

       obj-m := bq27501.o

#否則,,是直接從命令開始調(diào)用的,,這時(shí)要調(diào)用內(nèi)核構(gòu)造系統(tǒng)。

 

else

KDIR ?=/home/zl/ti-davinci

PWD := $(shellpwd)

CROSS_COMPILE=arm-v5t-le-

CC=$(CROSS_COMPILE)gcc

default:

       make -C $(KDIR) M=$(PWD) modules

endif

clean:

       rm -rf *.o *.cmd *.mod.c *.sysmvers

 

注:

1. Makefile文件的文件名中M一定要大寫,。這是因?yàn)榫幾g的時(shí)候首先看環(huán)境變量KERNELRELEASE是否定義,,如果沒定義則調(diào)用Linux內(nèi)核編譯build腳本。該腳本會(huì)首先編譯內(nèi)核,,其間會(huì)創(chuàng)建環(huán)境變量KERNELRELEASE,,接著編譯當(dāng)前工作目錄下的hello模塊,此時(shí)會(huì)第二遍讀取Makefile,再次判斷環(huán)境變量KERNELRELEASE是否定義,,已經(jīng)定義的情況下開始編譯hello模塊,。

2. Makefile文件中的命令行,以Tab鍵開頭(不能是空格),,例如make,,clean。依賴條件頂格,,例如default,clean,。

3. KDIR:指向嵌入系統(tǒng)的linux內(nèi)核,,而不是正在運(yùn)行的系統(tǒng)的內(nèi)核。

4.CROSS_COMPILE:交叉編譯環(huán)境,,也就是安裝的dvdsdk,。

5. CC:指明編譯器,加上CROSS_COMPILE,,定義了一個(gè)交叉編譯器,。

6. clean:當(dāng)時(shí)使用makeclean命令時(shí)清除編譯的結(jié)果。

7. default:當(dāng)使用make命令后面不加任何參數(shù)時(shí),,默認(rèn)執(zhí)行的語句,。

將驅(qū)動(dòng)代碼的源文件和Makefile文件放到同一個(gè)文件夾下面。在終端進(jìn)入到目錄下,,使用make命令對(duì)其進(jìn)行編譯,。

 

2)  模塊編程

      為了bq27501驅(qū)動(dòng)測(cè)試的方便,所以采用模塊編程的方式來實(shí)現(xiàn),。將bq27501驅(qū)動(dòng)作為一個(gè)內(nèi)核模塊動(dòng)態(tài)加載到內(nèi)核中,,而不是采用在內(nèi)核樹中添加代碼實(shí)現(xiàn)這種靜態(tài)的方法實(shí)現(xiàn)的。

第一,,模塊編程的程序的頭文件,。

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

這三個(gè)頭文件是編寫內(nèi)核模塊程序必須包含的3個(gè)頭文件。

        第二,,內(nèi)核模塊必須包含兩個(gè)函數(shù),。一個(gè)是加載時(shí)模塊初始化函數(shù),,另一個(gè)是模塊卸載時(shí)卸載的函數(shù)。bq27501的初始化函數(shù)為staticint __initbq27501_init(void),;module_init(bq27501_init),;初始化函數(shù)聲明為static,因?yàn)槌跏蓟瘮?shù)在特定文件文件之外沒有其他意義,,并且驅(qū)動(dòng)初始化函數(shù)前面加上__init標(biāo)記,,這表明該函數(shù)僅在初始化期間使用。在模塊被裝載之后,,模塊裝載器就會(huì)將初始化函數(shù)扔掉,,這樣可以將該函數(shù)占用的內(nèi)存釋放出來以作他用(注,不能在初始化之后仍要使用的函數(shù),,或者數(shù)據(jù)結(jié)構(gòu)),。module_init這個(gè)宏會(huì)在模塊的目標(biāo)代碼中增加一個(gè)特殊的段,用于說明內(nèi)核初始化函數(shù)所在的位置,。如果沒有這個(gè)定義,,那么初始化函數(shù)bq27501將不會(huì)被調(diào)用。所以是必須使用的module_init,。bq27501的卸載函數(shù)為staticvoid __exit bq27501_exit(void),;module_exit(bq27501_exit);該函數(shù)在模塊被卸載前注銷接口并向系統(tǒng)中返回所有資源,。卸載函數(shù)沒有返回值,,故要聲明為void。__exit標(biāo)記表示該代碼用于模塊卸載,。如果模塊被直接內(nèi)核的配置不允許卸載模塊,,則被標(biāo)記為__exit的函數(shù)將被簡(jiǎn)單地丟棄。所以被標(biāo)記為__exit的函數(shù)只能在模塊被卸載或者系統(tǒng)關(guān)閉時(shí)被調(diào)用,,其他任何用法都是錯(cuò)誤的,。和module_init類似,module_exit用于內(nèi)核可以找到模塊的卸載函數(shù),。如果模塊沒有定義卸載函數(shù),,那么內(nèi)核不允許卸載該模塊。

         第三,,模塊的聲明與描述,。MODULE_LICENSE("GPLv2");描述內(nèi)核模塊的許可權(quán)限,如果不聲明LICENSE,,模塊被加載時(shí),,將收到內(nèi)核的警告。 在Linux2.6 內(nèi)核中,,可接受的LICENSE包括“GPL”(任一版本的GNU通用公共許可證),,“GPL v2”(GPL版本2),,“GPLand additional rights”(MPL/GPL雙重許可證),,“DualBSD/GPL”(雙重許可證),,“DualMPL/GPL”,“Proprietary”,。 MODULE_AUTHOR("zl"); 聲明該模塊的作者,。MODULE_DESCRIPTION("BQ27501Driver");對(duì)該模塊功能簡(jiǎn)單的描述,。

以上是內(nèi)核模塊編程的三個(gè)特點(diǎn)。其他函數(shù)的編寫和調(diào)用和普通的GNU C一樣,。

3)  bq27501的i2c通信

       bq27501驅(qū)動(dòng)的核心和關(guān)鍵就是完成i2c的通信,,對(duì)于不同的設(shè)備i2c通信的消息格式可能不同,需要查閱對(duì)應(yīng)的數(shù)據(jù)手冊(cè),。從bq27501的數(shù)據(jù)手冊(cè)中獲得與其通信的message的格式,,如下圖(3)所示:


圖(3)  bq27501的i2c通信消息格式

選擇1-byte read消息格式,ADDR+CMD為一條寫消息,,ADDR+DATA為一條讀消息,。CMD為要讀信息的command code,DATA是返回的數(shù)據(jù),。例如電壓voltage的commandcode為0x08/0x09兩個(gè)字節(jié),,返回值為一個(gè)unsignedint型的值,即也為兩個(gè)字節(jié),,所以要讀取電壓值,要執(zhí)行兩次1-byte read,。構(gòu)造的i2c_msg格式如下面的代碼片段所示,。

  1. //the first "write" message:ADDR+CMD1  
  2.       msg.addr= client->addr;  
  3.       msg.flags= 0;  // the "write" flag  
  4.       msg.len= I2C_ONE_BYTE_TRANSFER;  
  5.       msg.buf= &cmd_data;  
  6.       cmd_data= reg;  
  7.       err= i2c_transfer(client->adapter,&msg, 1);  
  8.       if(err>=0){  
  9.              //thefirst "read" message: ADDR+DATA1  
  10.              msg.addr=client->addr;  
  11.              msg.flags=I2C_M_RD;             //the"read" flag      
  12.              msg.len=I2C_ONE_BYTE_TRANSFER;  
  13.              msg.buf= &data1;  
  14.              err=i2c_transfer(client->adapter, &msg, 1);  
  15.              if(err>=0){  
  16.                     //thesecond "write" message: ADDR+CMD2  
  17.                     msg.addr= client->addr;  
  18.                     msg.flags= 0;  
  19.                     msg.len= I2C_ONE_BYTE_TRANSFER;  
  20.                     msg.buf= &cmd_data;  
  21.                     cmd_data= reg+1;  
  22.                     err= i2c_transfer(client->adapter,&msg, 1);  
  23.                     if(err>=0){  
  24.                            //thesecond "read" message: ADDR+DATA2  
  25.                            msg.addr=client->addr;  
  26.                            msg.flags=I2C_M_RD;                     
  27.                            msg.len=I2C_ONE_BYTE_TRANSFER;  
  28.                            msg.buf= &data2;  
  29.                            err=i2c_transfer(client->adapter, &msg, 1);  
  30.                            if(err>=0){  
  31.                                   *val= ((data2& I2C_TXRX_DATA_MASK)  
  32.                                          <<I2C_TXRX_DATA_SHIFT)  
  33.                                          |(data1 & I2C_TXRX_DATA_MASK);  
  34.                                          }  
  35.                                   }                    
  36.                            }  
  37.                     }  
  38.    


4)  驅(qū)動(dòng)與用戶空間的數(shù)據(jù)交互

       內(nèi)核中的數(shù)據(jù)與用戶空間數(shù)據(jù)交互常用的函數(shù)有copy_to_user,copy_from_user,,和宏定義put_user,,get_user,__put_user,,__get_user,。copy_from_user和copy_to_user函數(shù)復(fù)制塊數(shù)據(jù),如數(shù)組,,結(jié)構(gòu)體,;put_user,get_user,,__put_user,,__get_user復(fù)制的內(nèi)存是簡(jiǎn)單類型,如char,,int,,long,,而且只能復(fù)制1,2,4,8個(gè)字節(jié)。put_user,,get_user,,__put_user,__get_user執(zhí)行效率比copy_to_user,,copy_from_user的效率要高很多,。put_user和__put_user區(qū)別在于,前者會(huì)調(diào)用access_ok進(jìn)行內(nèi)核地址的檢查,,而后者不進(jìn)行地址檢查,。bq27501驅(qū)動(dòng)的功能是向用戶空間提供電池相關(guān)的信息;而且不需要用戶空間向電池輸入數(shù)據(jù),;另外電池的信息都是很小的數(shù)據(jù),,都可以使用unsigned short表示,所以沒有必要使用copy_to_user塊數(shù)據(jù)傳遞的函數(shù),;另外再根據(jù)《Linux驅(qū)動(dòng)程序》書中“大多數(shù)驅(qū)動(dòng)程序代碼中都不需要access_ok,,內(nèi)存管理程序會(huì)處理它”,所以選擇__put_user來向用戶空間傳遞電池信息數(shù)據(jù),。

__put_user(var, ptr),,var將內(nèi)核中的數(shù)據(jù)var復(fù)制到用戶空間;ptr是用戶地址空間的指針,,指向內(nèi)核空間中ioctl最后一個(gè)參數(shù),。bq27501與用戶空間交互的代碼片段如下:

  1. static long bq27501_ioctl(struct file*file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.       BATT_INFO*pBattInfo = &BattInfo;  
  4.       unsignedshort temp;  
  5.       switch(cmd)  
  6.       {  
  7.              caseGET_RM:  
  8.                     if((i2c_read_reg(&bq27501_i2c_client,RM_REG_ADDR_L,&pBattInfo->RemainingCapacity))>=0){  
  9.                            printk(KERN_ALERT"RaminingCapacityis %u mAh ...\n",pBattInfo->RemainingCapacity);  
  10.                            __put_user(pBattInfo->RemainingCapacity,(int __user *)arg);  
  11.                     }  
  12.                     break;  
  13. ……  
  14. }  


在用戶空間, ioctl 系統(tǒng)調(diào)用的原型為:intioctl(int fd, unsigned long cmd, ...); 這個(gè)原型中的點(diǎn)表示函數(shù)有一個(gè)單個(gè)可選的參數(shù), 傳統(tǒng)上標(biāo)識(shí)為 char *argp. 這些點(diǎn)在那里只是為了阻止在編譯時(shí)的類型檢查。第二個(gè)參數(shù),,是用戶向驅(qū)動(dòng)傳遞的命令(如讀取剩余電量值),。第三個(gè)參數(shù)的實(shí)際特點(diǎn)依賴所發(fā)出的特定的控制命令,即第二個(gè)參數(shù),。一些命令不用參數(shù), 一些用一個(gè)整數(shù)值, 以及一些使用指向其他數(shù)據(jù)的指針,。是否使用參數(shù)和指針是根據(jù)打開字符設(shè)備的方式?jīng)Q定,打開設(shè)備的方式有三種,,在fcntl.h有其宏定義,,只讀,只寫,,讀寫,,如下所示:

  1. #define O_RDONLY         00  
  2. #define O_WRONLY         01  
  3. #define O_RDWR           02  
  4. 用戶空間讀取驅(qū)動(dòng)中參數(shù)代碼片段如下:  
  5. if((fd = open("/dev/bq27501",2)) == -1){  
  6.     perror("device open err!\n");  
  7.     return -1;  
  8. }  
  9. printf("open ok!\n");  
  10. ioctl(fd,atoi(argv[1]),&v); //v是一個(gè)整型,用于保存內(nèi)核空間傳遞過來的數(shù)據(jù)  
  11. printf("(2)........cmd = [%d],val = [%d]...........\n",atoi(argv[1]),v);  


 

六,、        bq27501驅(qū)動(dòng)的測(cè)試

1. 測(cè)試目的:

驗(yàn)證bq27501驅(qū)動(dòng)能否讀取寄存器的值,,和用戶空間的交互。

 

2. 測(cè)試過程和結(jié)果:

        bq27501驅(qū)動(dòng)的測(cè)試是要在用戶空間編寫測(cè)試程序。Linux將所有設(shè)備都作為文件來處理的,,所以在用戶空間必須打開bq27501設(shè)備文件,。fd =open("/dev/bq27501",2);這里使用open打開在dev下創(chuàng)建的bq27501的字符設(shè)備,,“2”表示以讀寫的方式打開(也可以用O_RDWR這個(gè)宏來表示),,fd用來保存文件句柄。通過ioctl向設(shè)備文件發(fā)送命令,,并接收返回的電池統(tǒng)計(jì)信息值,,ioctl(fd,atoi(argv[1]),&v);fd是打開的設(shè)備文件的句柄,atoi(argv[1])是將main的參數(shù)轉(zhuǎn)換成整型作為參數(shù)傳給驅(qū)動(dòng),,v用來保存驅(qū)動(dòng)返回的數(shù)值,。

       測(cè)試程序編寫完成之后,因?yàn)橐贒M355平臺(tái)上運(yùn)行,,所以必須要交叉編譯源文件testbq.c,。使用交叉編譯環(huán)境dvsdk下的命令arm_v5t_le-gcc testbq.c –o testbq,生成可執(zhí)行的testbq文件。

        使用make命令編譯驅(qū)動(dòng)的源文件bq27501.c,,生成bq27501.ko文件,。在DM355中使用tftp命令tftp –g–r bq27501.ko 10.10.101.138”下載bq27501.ko到開發(fā)板上(10.10.101.138為tftp服務(wù)器的地址)。使用insmod命令加載驅(qū)動(dòng)模塊,,即“insmod bq27501.ko”,。加載成功會(huì)打印添加的信息如下:

registersucceed!...

bq27501_driver->id225 ....

I2C:detect address is 55 ...

Adaptername is DAVINCI I2Cadapter..

Adddriver succeed!...

使用mknod命令為bq27501創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn),即命令“mknod /dev/bq27501 c 225 0”,。節(jié)點(diǎn)的名稱為bq27501,,是在/dev目錄下創(chuàng)建的;c表示該節(jié)點(diǎn)是一個(gè)字符設(shè)備,;225表示主設(shè)備號(hào),,這個(gè)必須和驅(qū)動(dòng)中注冊(cè)時(shí)的一樣;0表示從設(shè)備號(hào),。

在DM355中使用tftp命令“tftp –g –r testbq 10.10.101.138”下載testbq到開發(fā)板上,。使用命令“chmod 777 testbq”修改執(zhí)行的權(quán)限,。使用命令“./testbq 3”執(zhí)行testbq文件,,“3”是傳給main函數(shù)的參數(shù),表示獲得電池的電壓值,。運(yùn)行結(jié)果為“Voltage is 3779 mV ... ”,,即3.78v,和電池的額定輸出電壓3.7一樣,。另外還測(cè)試了溫度“Temperature is 2974 k...”,,即24.25攝氏度;剩余電量可用的時(shí)間“Time to Empty is 65535 min..”,即65535分鐘,,表示電池沒有處于放電狀態(tài),,符合實(shí)際情況。

根據(jù)測(cè)試的結(jié)果,,可以說明bq27501驅(qū)動(dòng)和用戶測(cè)試程序的基本功能點(diǎn)已經(jīng)正確實(shí)現(xiàn),。

七、        總結(jié)

1. 不能省略用戶空間測(cè)試文件的編寫,,在初始化函數(shù)中調(diào)用i2c_read_reg函數(shù)測(cè)試驅(qū)動(dòng)i2c通信是否成功,。模塊初始化module_init調(diào)用初始化函數(shù)bq27501_init函數(shù)需要系統(tǒng)調(diào)用sys_init_module()。sys_init_module要得到bq27501_init的返回值才能完成模塊的初始化,。sys_init_module中有這么段代碼:

  1. /*Start the module */  
  2. if(mod->init != NULL)  
  3.        ret= do_one_initcall(mod->init);  
  4. if(ret < 0) {  
  5.        /*Init routine failed: abort.  Try toprotect us from 
  6.              buggy refcounters. */  
  7.        mod->state= MODULE_STATE_GOING;  
  8.        synchronize_sched();  
  9.        module_put(mod);  
  10.        blocking_notifier_call_chain(&module_notify_list,  
  11.                                  MODULE_STATE_GOING, mod);  
  12.        mutex_lock(&module_mutex);  
  13.        free_module(mod);  
  14.        mutex_unlock(&module_mutex);  
  15.        wake_up(&module_wq);  
  16.        returnret;  
  17. }  
  18. if(ret > 0) {  
  19.        printk(KERN_WARNING"%s: '%s'->init suspiciously returned %d, "  
  20.                          "it should follow 0/-Econvention/n"  
  21.               KERN_WARNING "%s: loading moduleanyway.../n",  
  22.               __func__, mod->name, ret,  
  23.               __func__);  
  24.        dump_stack();  
  25. }  


從上面的代碼可以看出,,模塊的init函數(shù)只能返回0或者負(fù)的錯(cuò)誤碼,否則會(huì)提示錯(cuò)誤,。所以在return語句之前調(diào)用模塊中的其他函數(shù)時(shí)不正確的,,因?yàn)檫@時(shí)模塊還未真正初始化完成。

2. 在編寫bq27501驅(qū)動(dòng)過程中最常見的錯(cuò)誤是“segmentation fault”,。該錯(cuò)誤的原因是訪問了非法的內(nèi)存,,也就是使用了空指針或者未給一個(gè)指針變量分配內(nèi)存空間就直接使用。在內(nèi)核中對(duì)一個(gè)結(jié)構(gòu)體的指針變量,,要先使用kmalloc函數(shù)為其分配空間,,然后才能賦值。

3. 嵌入式linux系統(tǒng)中,,由于內(nèi)存較小,,可以將需要在多個(gè)函數(shù)之間傳遞的參數(shù)的變量設(shè)為全局變量,減少相同變量的申請(qǐng),,這樣就可以節(jié)省內(nèi)存資源,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多