怯肇乾,吳志亮
(鄭州精益達(dá)汽車零部件有限公司,河南 鄭州 100044)
廣泛應(yīng)用的ARM-Linux及其Android的嵌入式軟硬件系統(tǒng)中,常常涉及到內(nèi)部整合電路IIC (Inter-Integrated Circuit)總線的操作及其設(shè)備的添加與驅(qū)動(dòng)的實(shí)現(xiàn)?;贏RM內(nèi)核的各類微處理器芯片中,通常都會(huì)集成一到幾個(gè)IIC總線控制器,用以連接存儲(chǔ)器、傳感器、人機(jī)界面等IIC設(shè)備。沒(méi)有操作系統(tǒng)的常規(guī)應(yīng)用中,根據(jù)IIC控制器和所選IIC設(shè)備的特點(diǎn),按照IIC通信協(xié)議,通過(guò)直接明了的編程設(shè)計(jì),很容易在系統(tǒng)中實(shí)現(xiàn)IIC設(shè)備的添加與驅(qū)動(dòng)。而ARM-Linux嵌入式操作系統(tǒng),對(duì)IIC采用分層分散的總線架構(gòu),雖然做到了穩(wěn)定、高效、模塊化,卻使向其中添加需要的IIC設(shè)備及其驅(qū)動(dòng)實(shí)現(xiàn)困難了很多,常規(guī)的驅(qū)動(dòng)設(shè)計(jì)方法不能用了。ARMLinux下,IIC總線的體系框架是怎樣的?如何因地制宜向其中添加IIC設(shè)備并順利實(shí)現(xiàn)其驅(qū)動(dòng)程序設(shè)計(jì)呢?本文以下將展開(kāi)詳細(xì)闡述。
ARM-Linux下,IIC分為3個(gè)層次:IIC內(nèi)核、IIC總線驅(qū)動(dòng)和IIC設(shè)備驅(qū)動(dòng)。IIC內(nèi)核提供核心數(shù)據(jù)結(jié)構(gòu)的定義和相關(guān)接口函數(shù),實(shí)現(xiàn)驅(qū)動(dòng)的注冊(cè)、注銷管理,設(shè)備的探測(cè)、檢查,以及無(wú)關(guān)具體適配器的讀寫通信代碼。IIC總線驅(qū)動(dòng)定義具體的適配器結(jié)構(gòu)i2c_adapter及其算法結(jié)構(gòu)i2c_algorithm,控制適配器以主控方式產(chǎn)生IIC通信時(shí)序。IIC內(nèi)核和IIC總線驅(qū)動(dòng)共同完成硬件上的主機(jī)總線控制器驅(qū)動(dòng)。IIC設(shè)備驅(qū)動(dòng)定義描述具體設(shè)備的i2c_client,以具體的i2c_driver實(shí)現(xiàn)從機(jī)設(shè)備驅(qū)動(dòng),包括read、write以及ioctl等用戶層接口。ARMLinux-IIC總線驅(qū)動(dòng)的框架結(jié)構(gòu)及其所在的軟/硬件體系如圖1所示。
圖1 ARM-Linux-IIC總線驅(qū)動(dòng)的框架結(jié)構(gòu)圖Fig.1 A framework drawing of ARM-Linux-IICbus driving
從圖1可以看出,在ARM-Linux中添加IIC設(shè)備,首先是編寫針對(duì)該設(shè)備的具體i2c_driver驅(qū)動(dòng)程序。然后是構(gòu)造描述該IIC設(shè)備的i2c_client,并向系統(tǒng)注冊(cè)。構(gòu)造的i2c_client是“橋梁”,完成指定i2c_adapter主機(jī)適配控制器與特定i2c_driver設(shè)備驅(qū)動(dòng)的有機(jī)聯(lián)結(jié),從而實(shí)現(xiàn)需要的IIC主從串行通信。
一般地,ARM-Linux操作系統(tǒng)提供有實(shí)現(xiàn)了IIC內(nèi)核框架的i2c/i2c-core.c文件及其包括GPIO模擬IIC在內(nèi)的常用IIC各類通信算法(在目錄i2c/algos/下),還有適用于大多數(shù)IIC從設(shè)備的i2c/i2c-dev.c文件。針對(duì)具體的ARM內(nèi)核微處理器體系, 如 Samsung-CortexA8 的 S5PV110/S5PV210、TICortexA8 的 AM3515/AM3715、Freescale -CortexA8 的 i.MX515/i.MX535等,半導(dǎo)體廠家通常都為自己具體的IIC總線控制器提供有適配器驅(qū)動(dòng) (在目錄i2c/algos/下,如i2cs3c2410.c)。
綜上所述,在ARM-Linux中增加IIC新設(shè)備,可以借用IIC通用驅(qū)動(dòng)i2c-dev.c快速實(shí)現(xiàn),可以為其編寫特定的IIC驅(qū)動(dòng)。ARM-Linux設(shè)備驅(qū)動(dòng)通常采用靜態(tài)加載操作,它有兩種方式:適配 (adapter)和探測(cè) (probe),適配是傳統(tǒng)方式(Legacy),探測(cè)是新類型(New Style),大多數(shù)設(shè)備驅(qū)動(dòng)越來(lái)越趨向采用probe方式。也可以采用驅(qū)動(dòng)設(shè)計(jì)人員習(xí)慣的動(dòng)態(tài)加載方式編寫簡(jiǎn)易的IIC“客服-驅(qū)動(dòng)”。IIC總線,可以采用系統(tǒng)硬件實(shí)現(xiàn)的IIC總線控制器,也可以選用通用輸入-輸出端口GPIO模擬產(chǎn)生。如果理解不了ARM-Linux的IIC總線層次框架,也可以拋掉這種機(jī)制采用傳統(tǒng)方式編寫IIC字符型設(shè)備驅(qū)動(dòng)程序。下面就上述幾種方法,分別說(shuō)明ARM-Linux-IIC設(shè)備的具體添加與驅(qū)動(dòng)實(shí)現(xiàn)過(guò)程。
需要注意的是,ARM-Linux中,由于IIC總線及其控制器使用的普遍,默認(rèn)系統(tǒng)是加載IIC總線適配器的。如果選用的平臺(tái)沒(méi)有采用己有的IIC總線及其控制器,使用ARMLinux-IIC總線驅(qū)動(dòng)框架,首先要在編譯形成系統(tǒng)映像文件前,通過(guò)menuconfig配置選擇IIC及其相應(yīng)的適配器驅(qū)動(dòng),還有通用或特定的IIC設(shè)備驅(qū)動(dòng)。
采用i2c-dev.c直接驅(qū)動(dòng)新添加的IIC設(shè)備是最簡(jiǎn)便的方法,它實(shí)際上是通過(guò)在應(yīng)用層操作IIC適配器來(lái)控制i2c設(shè)備的。i2c-dev.c是通用的IIC設(shè)備驅(qū)動(dòng),它提供有通用的read()、write()和 ioctl()等設(shè)備文件操作接口,應(yīng)用層可以借用這些接口訪問(wèn)掛接在適配器上的IIC設(shè)備的存儲(chǔ)空間或寄存器,并控制IIC設(shè)備的工作方式。需要注意的是,i2c-dev.c對(duì)應(yīng)的read()和write()是分別調(diào)用IIC內(nèi)核心的i2c_master_recv()和 i2c_master_send()函數(shù),通過(guò)構(gòu)造一條 IIC 消息并引發(fā)適配器algorithm通信函數(shù)的調(diào)用來(lái)完成消息傳輸?shù)?,而大多?shù)稍微復(fù)雜一點(diǎn)IIC設(shè)備的讀寫流程并不對(duì)應(yīng)于一條消息,往往需要兩條甚至更多的消息來(lái)進(jìn)行一次讀寫周期,即具有IIC重復(fù)開(kāi)始位RepStart的情況,此時(shí)將不能正確地讀寫。對(duì)于兩條以上消息組成的讀寫即IIC總線RepStart的情況,需要采用ioctl(),組織i2c_msg消息數(shù)組并調(diào)用I2C_RDWR IOCTL命令來(lái)完成。下面以IIC接口無(wú)線射頻RFID卡讀寫模塊的標(biāo)識(shí)字節(jié)設(shè)置操作為例,簡(jiǎn)單加以說(shuō)明。
采用read()和write()操作的主要程序代碼如下:
int main(void)
{ unsigned int fd; unsigned char buf[10];
fd=open("/dev/i2c-1", O_RDWR);
if(!fd) return 0;
ioctl(fd, I2C_SLAVE, 0x58); //設(shè)置從機(jī)設(shè)備地址
ioctl(fd, I2C_TIMEOUT, 1); //設(shè)置超時(shí)值
ioctl(fd, I2C_RETRIES, 1); //設(shè)置重試次數(shù)
buf[0]=0x03; buf[1]=0x13;buf[2]=0x13;buf[3]=0x03;
write(fd, buf, 4); //RFID 卡標(biāo)識(shí)字節(jié)設(shè)置
read(fd, buf, 3); //回讀,以判斷設(shè)置是否成功
close(fd); return 0;
}
采用ioctl()操作的主要程序代碼如下,這里實(shí)現(xiàn)面向3個(gè)連續(xù)的IIC從機(jī)進(jìn)行寫入:
int main(void)
{ struct i2c_rdwr_ioctl_data work_queue;
unsigned int i, fd, buf[4]= {0x03, 0x13, 0x13,0x03};
fd=open("/dev/i2c-1", O_RDWR);
if(!fd) return 0;
work_queue.nmsgs=3; //消息數(shù)量
work_queue.msgs= (struct i2c_msg*)
malloc (work_queue.nmsgs*sizeof (struct i2c_msg));
for(i=0; i< work_queue.nmsgs; ++i)
{ work_queue.msgs[i].len=4; //數(shù)據(jù)長(zhǎng)度
work_queue.msgs[i].addr=0x58+i;//IIC 從機(jī)地址
work_queue.msgs[i].buf=buf; //數(shù)據(jù)指針
work_queue.msgs[i].flags=I2C_M_WR;//寫命令
}
ioctl(fd, I2C_TIMEOUT, 2); //設(shè)置超時(shí)
ioctl(fd, I2C_RETRIES, 1); //設(shè)置重試次數(shù)
ioctl(fd,I2C_RDWR,(unsigned long)&work_queue);
close(fd); return 0;
}
probe方式的驅(qū)動(dòng),供ARM-Linux-IIC驅(qū)動(dòng)體系,在系統(tǒng)啟動(dòng)后進(jìn)行IIC基本功能性探測(cè)并加載,在系統(tǒng)關(guān)閉時(shí)進(jìn)行“去除”缷載。這種類型的驅(qū)動(dòng)基本上分為3部分:面向用戶的設(shè)備文件操作、面向IIC體系的驅(qū)動(dòng)探測(cè)與去除、設(shè)備模塊的加載初始化與缷載去除,設(shè)計(jì)的關(guān)鍵在于具體i2c_client的構(gòu)造和ic2_driver的實(shí)例化。上述IIC接口RFID卡讀寫模塊probe方式的主要驅(qū)動(dòng)程序代碼編寫如下:
static struct i2c_client*rfid_client; //IIC客戶
static const struct i2c_device_id rfid_i2c_id[]={{"rfid_iic",0}};//IIC設(shè)備標(biāo)識(shí)
static int rfid_open(struct inode*inode, struct file*filp )
//設(shè)備文件打開(kāi)
{ filp->private_data=NULL;
try_module_get(THIS_MODULE); return 0;
}
static int rfid_release(struct inode*inode,struct file*filp)
//設(shè)備文件關(guān)閉
{ filp->private_data=NULL;
module_put(THIS_MODULE); return 0;
}
static int rfid_write (struct file*filp, const char*buffer,size_t count, loff_t*offset) //設(shè)備寫操作
{ char*tmp; int ret;
tmp=kzalloc(count, GFP_KERNEL);
//內(nèi)核數(shù)據(jù)空間分配
if(tmp==NULL) return -ENOMEM;
if(copy_from_user(tmp, buffer, count))
//數(shù)據(jù)拷貝:用戶空間-->內(nèi)核空間
{kfree(tmp); return -EFAULT; }
ret=i2c_master_send(rfid_client, tmp, count);
//調(diào)用系統(tǒng)函數(shù),完成數(shù)據(jù)發(fā)送
kfree(tmp); return ret;
}
static int rfid_read (struct file*filp, char*buffer, size_t count,loff_t*offset) //設(shè)備讀操作
{ char *tmp; int ret;
tmp=kzalloc(count, GFP_KERNEL);
//內(nèi)核數(shù)據(jù)空間分配
if(tmp==NULL)return -ENOMEM;
ret=i2c_master_recv(rfid_client, tmp, count);
//調(diào)用系統(tǒng)函數(shù),執(zhí)行數(shù)據(jù)接收
if(copy_to_user(buffer, tmp, count))
//數(shù)據(jù)拷貝:內(nèi)核空間-->用戶空間
{kfree(tmp); return -EFAULT; }
kfree(tmp); return ret;
}
static structfile_operationsrfid_fops=//設(shè)備文件系統(tǒng)實(shí)例化
{ owner:THIS_MODULE,
read: rfid_read,
write:rfid_write,
open:rfid_open,
release: rfid_release,
};
static int__devinit rfid_probe (struct i2c_client*client,const struct i2c_device_id*id)//IIC設(shè)備探測(cè)
{ if (!i2c_check_functionality (client->adapter,I2C_FUNC_I2C)) return -ENODEV;
return 0;
}
static int rfid_remove(struct i2c_client*client)
//IIC設(shè)備去除
{ kfree(rfid_client);
i2c_set_clientdata(client, NULL); return 0;
}
static struct i2c_driver rfid_i2c_driver=
//IIC設(shè)備驅(qū)動(dòng)實(shí)例化
{ .driver={.name="rfidDriver",}, //驅(qū)動(dòng)命名
.probe=rfid_probe, //設(shè)備探測(cè)
.remove=__devexit_p(rfid_remove), //設(shè)備去除
.id_table=rfid_i2c_id, //設(shè)備信息標(biāo)識(shí)
};
static int__init rfid_drv_init(void) //IIC 設(shè)備初始化
{ int ret;
struct i2c_board_info info;
struct i2c_adapter *adapter;
adapter=i2c_get_adapter(1);
//選擇 IIC 適配器號(hào)(0~2)
memset(&info,0,sizeof(struct i2c_board_info)); // 構(gòu)造IIC設(shè)備(板信息-->IIC客戶)
strlcpy(info.type, "rfidClient", I2C_NAME_SIZE);
//設(shè)備名字
info.addr=0x58; //從機(jī)地址
rfid_client=i2c_new_device(adapter, &info);
//注冊(cè)特定的i2c_client
if(!rfid_client) return -ENODEV;
ret=register_chrdev(250, "rfidIIC", &rfid_fops);
//注冊(cè)字符設(shè)備
if(ret==0)
{ ret=i2c_add_driver(&rfid_i2c_driver);
//指定特定IIC設(shè)備驅(qū)動(dòng)
if(ret==0) printk("Rfid(0x058)_Creation success! ");
}
else unregister_chrdev(250, "rfidIIC");
return ret;
}
static void__exit rfid_drv_exit(void) //IIC設(shè)備缷載
{ i2c_del_driver(&rfid_i2c_driver);//注銷 IIC 驅(qū)動(dòng)
unregister_chrdev(250, "rfidIIC");
//注銷字符設(shè)備
}
MODULE_LICENSE("GPL");
module_init(rfid_drv_init); //Linux 模塊初始化
module_exit(rfid_drv_exit); //Linux 模塊去除
動(dòng)態(tài)加載形式的設(shè)備驅(qū)動(dòng),便于調(diào)試,用時(shí)掛載,不用時(shí)隨時(shí)缷載,既使運(yùn)行時(shí),因而廣泛采用。將probe方式IIC設(shè)備驅(qū)動(dòng)的xxx_probe()和xxx_remove()函數(shù)分別合并到xxx_init()和xxx_exit()函數(shù),就可以得到動(dòng)態(tài)加載形式的IIC設(shè)備驅(qū)動(dòng)。i2c_client的構(gòu)造仍是IIC設(shè)備驅(qū)動(dòng)具體化的關(guān)鍵,因此特別稱這種類型的驅(qū)動(dòng)為簡(jiǎn)易“客服-驅(qū)動(dòng)”型設(shè)備驅(qū)動(dòng)。對(duì)上述IIC接口RFID卡讀寫模塊probe方式的驅(qū)動(dòng)作簡(jiǎn)易“客服-驅(qū)動(dòng)”型設(shè)備驅(qū)動(dòng)改造,變化部分的主要程序代碼如下:
static int__init rfid_drv_init(void) //IIC 設(shè)備初始化
{ int ret;
struct i2c_board_info info;
struct i2c_adapter *adapter;
if(!i2c_check_functionality(client->adapter,
//IIC設(shè)備基本功能性探測(cè)
I2C_FUNC_I2C))return-ENODEV;
adapter=i2c_get_adapter(1);
//選擇 IIC 適配器號(hào)(0~2)
memset(&info,0,sizeof(struct i2c_board_info));
//構(gòu)造IIC設(shè)備(板信息-->IIC客戶)
strlcpy(info.type, "rfidClient", I2C_NAME_SIZE);
//設(shè)備名字
info.addr=0x58; //從機(jī)地址
rfid_client=i2c_new_device(adapter, &info);
//注冊(cè)特定的i2c_client
if(!rfid_client) return -ENODEV;
ret=register_chrdev(250, "rfidIIC", &rfid_fops);
//注冊(cè)字符設(shè)備
if(ret<0) unregister_chrdev(250, "rfidIIC");
return ret;
}
static void__exit rfid_drv_exit(void) //IIC 設(shè)備缷載
{ kfree(rfid_client); //i2c_client描述釋放
unregister_chrdev(250, "rfidIIC"); //注銷字符設(shè)備
}
MODULE_LICENSE("GPL");
module_init(rfid_drv_init); //Linux 模塊初始化
module_exit(rfid_drv_exit); //Linux 模塊去除
可以看到,相對(duì)probe方式的IIC設(shè)備驅(qū),簡(jiǎn)易“客服-驅(qū)動(dòng)”型IIC設(shè)備驅(qū)動(dòng),沒(méi)有了xxx_i2c_driver及其相關(guān)函數(shù)的實(shí)例化設(shè)計(jì),整個(gè)程序框架結(jié)構(gòu)簡(jiǎn)便多了,可設(shè)計(jì)性與可讀性增加了。
選擇GPIO端口模擬IIC總線驅(qū)動(dòng)IIC設(shè)備,雖然對(duì)于系統(tǒng)整體效率不高,但是直截了當(dāng),易于操作實(shí)現(xiàn)??梢圆捎肁RM-Linux已有的GPIO模擬程序,也可以選擇GPIO自行獨(dú)立設(shè)計(jì)。這里仍以上述IIC接口RFID卡讀寫模塊為例,自選GPIO模擬IIC時(shí)序,以動(dòng)態(tài)加載形式的字符型IIC簡(jiǎn)易“客服-驅(qū)動(dòng)”設(shè)計(jì),加以說(shuō)明。主要程序代碼如下:
#define ByteDelayTimeout 0x0700; //字節(jié)傳輸超時(shí)值
#define BitDelayTimeout 0x1000; //位傳輸超時(shí)值
#define SCL_H{gpio_set_value (S5PV210_GPG3(5), 1);} //IIC串行時(shí)鐘線模擬
#define SCL_L{gpio_set_value (S5PV210_GPG3(5),0); }
#define SDA_H {gpio_set_value (S5PV210_GPG3(6),1);} //IIC串行數(shù)據(jù)線模擬
#define SDA_L{gpio_set_value(S5PV210_GPG3(6),0);}
#define SDA_IN {gpio_direction_input(S5PV210_GPG3(6));} //IIC 串行數(shù)據(jù)位輸入輸出
#define SDA_OUT {gpio_direction_output(S5PV210_GPG3(6),1); }
#define WHILE_SDA_HIGH (gpio_get_value (S5PV210_GPG3(6)))
static void ByteDelay(void) //字節(jié)傳輸延時(shí)
{ volatile unsigned int dwTimeout;
dwTimeout=ByteDelayTimeout;
while (--dwTimeout) {asm ("nop") ;}
}
static void BitDelay(void) //位傳輸延時(shí)
{ volatile unsigned int dwTimeout;
dwTimeout=BitDelayTimeout;
while ( --dwTimeout) {asm("nop"); }
}
static void I2C_Start(void) //IIC 傳輸啟動(dòng)位模擬
{ SDA_OUT; SDA_H; BitDelay();
SCL_H; BitDelay(); SDA_L; BitDelay();
}
static void I2C_Stop(void) //IIC傳輸停止位模擬
{ SDA_OUT; SDA_L; BitDelay();
SCL_H; BitDelay(); SDA_H; BitDelay();
}
static void I2C_Ack(void) //IIC 傳輸響應(yīng)位
{ SDA_OUT; SDA_L; BitDelay();
SCL_H; BitDelay(); SCL_L;
BitDelay(); SDA_IN; BitDelay();
}
static void I2C_Ack1(void) //IIC傳輸響應(yīng)位(帶延時(shí))
{ int i=0;
SCL_H; BitDelay(); SDA_IN;
while((WHILE_SDA_HIGH)&&(i<255)) i++; //無(wú)
應(yīng)答延時(shí)一段時(shí)間后默認(rèn)已經(jīng)收到
SCL_L; BitDelay(); SDA_OUT; BitDelay();
}
static void I2C_Nack(void) //IIC傳輸非響應(yīng)位
{ SDA_OUT; SDA_H; BitDelay();
SCL_H; BitDelay(); SCL_L;
BitDelay(); SCL_H;
}
static char Write_I2C_Byte(char byte)
//IIC總線字節(jié)寫操作
{ char i;
SCL_L; BitDelay ();
for(i=0 ; i<8; i++)
{if((byte&0x80)==0x80) SDA_H;
else SDA_L;
BitDelay(); SCL_H; BitDelay();
SCL_L; BitDelay(); byte <<=1;
}
return 1;
}
static char Read_I2C_Byte(void) //IIC總線字節(jié)讀操作
{ char i, buff=0;
SCL_L; BitDelay();
for(i=0; i< 8; i++)
{SDA_OUT;SDA_H;BitDelay();
SCL_H; SDA_IN; BitDelay();
if(WHILE_SDA_HIGH) buff|=0x01;
else buff&=~0x01;
if(i<7) buff<<=1 ;
SCL_L; BitDelay();
}
return buff;
}
static void InterfaceInit(void) //IIC接口的GPIO定義
{ gpio_direction_output(S5PV210_GPG3(5), 1);
//SCL OUT
gpio_direction_output(S5PV210_GPG3(6), 1);
//SDA OUT
gpio_set_value (S5PV210_GPG3(5), 1);
//初始高電平
gpio_set_value (S5PV210_GPG3(6), 1);
ByteDelay(); ByteDelay(); ByteDelay();
}
static int dvcIIC_open (struct inode*inode_ptr, struct file
*fptr) //設(shè)備文件打開(kāi)
{ fptr->f_op=&dvcIIC_fops;
fptr->private_data=NULL;
try_module_get(THIS_MODULE); return 0;
}
static int dvcIIC_release (struct inode*inode_ptr, struct file*fptr)//設(shè)備文件關(guān)閉
{ //fptr->private_data=NULL;
module_put(THIS_MODULE); return 0 ;
}
static int dvcIIC_read(struct file*fptr, char*buffer, size_t count,loff_t*fp) //設(shè)備文件讀
{ int i;char data[100];
I2C_Start(); //啟動(dòng)IIC傳輸,發(fā)送從機(jī)設(shè)備地址
Write_I2C_Byte((0x58<<1)+1);
I2C_Ack1();
for(i=0;i<count;i++) //按字節(jié)讀入數(shù)據(jù)
{data[i]=Read_I2C_Byte();I2C_Ack();}
I2C_Nack();I2C_Stop();
//發(fā)送非響應(yīng)位/停止位,結(jié)束操作
if(copy_to_user(buffer,data,count))return-1;
//向用戶空間回傳數(shù)據(jù)
else return 0;
}
static int dvcIIC_write(struct file*fptr,const char*buffer,size_t size,loff_t*fp)//設(shè)備文件寫
{ int i;char data[100];
if(copy_from_user(data,buffer,size))return-1;
//從用戶空間接收數(shù)據(jù)
I2C_Start(); //啟動(dòng)IIC傳輸,發(fā)送從機(jī)設(shè)備地址
Write_I2C_Byte((0x58<<1));I2C_Ack1();
for(i=0;i<size;i++) //按字節(jié)寫入數(shù)據(jù)
{Write_I2C_Byte(data[i]);I2C_Ack1();}
I2C_Stop();I2C_Nack(); //發(fā)送非響應(yīng)位/停止位,結(jié)束操作
I2C_Stop();return 0;
}
static struct file_operations dvcIIC_fops=//設(shè)備文件操作接口
{ .owner=THIS_MODULE,
.open=dvcIIC_open,
.read=dvcIIC_read,
.write=dvcIIC_write,
.release=dvcIIC_release,
};
static int dvcIIC_init(void)//IIC設(shè)備初始化:字符設(shè)備注冊(cè)
{ int status;
InterfaceInit();
status=register_chrdev(239,"dvcIIC",&dvcIIC_fops);
if(status<0)return status;
return 0;
}
static void dvcIIC_exit(void) //IIC設(shè)備注銷
{ unregister_chrdev(239,"dvcIIC");}
MODULE_LICENSE("GPL");
module_init(dvcIIC_init); //驅(qū)動(dòng)模塊初始化
module_exit(dvcIIC_exit); //驅(qū)動(dòng)模塊去除
ARM-Linux-IIC設(shè)備的添加與驅(qū)動(dòng),可以根據(jù)主機(jī)控制器與從機(jī)設(shè)備的特點(diǎn)采用傳統(tǒng)字符型設(shè)備驅(qū)動(dòng)的方式實(shí)現(xiàn);也可以根據(jù)ARM-Linux-IIC總線設(shè)備層次驅(qū)動(dòng)的軟件特點(diǎn),對(duì)常規(guī)IIC設(shè)備采用通用i2c-dev.c快速驅(qū)動(dòng),對(duì)特定IIC設(shè)備設(shè)計(jì)probe流行方式的驅(qū)動(dòng)或動(dòng)態(tài)加載的簡(jiǎn)易IIC“客服-驅(qū)動(dòng)”;還可以選用GPIO模擬IIC總線快速驅(qū)動(dòng)設(shè)備。其中,根據(jù)ARM-Linux-IIC層次驅(qū)動(dòng)的總線架構(gòu)設(shè)計(jì)IIC設(shè)備驅(qū)動(dòng),稍顯繁瑣,不易理解,但更符合ARM-Linux總線驅(qū)動(dòng)模塊化的規(guī)范,能夠有效地融入ARM-Linux體系,充分利用Linux內(nèi)核資源,實(shí)現(xiàn)Linux系統(tǒng)的穩(wěn)定、高效,是應(yīng)該主動(dòng)選擇與推薦使用的。
[1]怯肇乾.基于底層硬體的軟件設(shè)計(jì)[M].北京:航空航天大學(xué)出版社,2008.
[2]宋寶華.Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解[M].北京:人民郵電出版社,2008.
[3]劉紅波.實(shí)例解析Linux內(nèi)核I2C體系結(jié)構(gòu) [EB/OL](2009-12).http://www.dzsc.com/data/html/2009-12-22/81040.html.
[4]杜博,方向忠.嵌入式Linux系統(tǒng)下I2C設(shè)備驅(qū)動(dòng)程序的開(kāi)發(fā)[J].微計(jì)算機(jī)信息,2006,22(11):21-23.DU Bo,F(xiàn)ANG Xiang-zhong.I2Cdevice driver development in embedded Linux OS[J].Microcomputer Information,2006,22(11):21-23.
[5]李力,厲謹(jǐn).利用GPIO模擬I2C總線協(xié)議 [J].科技風(fēng),2009(12):33-36.LI Li,LI Jin.Simulating I2C bus communication with GPIO[J].Science-Technology Wind,2009(12):33-36.
[6]楊文鉑,刑鵬康.Linux下I2C設(shè)備驅(qū)動(dòng)的一種適配器層直接實(shí)現(xiàn)方法[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2011,11(6):16-19.YANGWen-bo,XING Peng-kang.A I2Cdevice driver method about adapter layer in linux OS[J].Single Chip Microcomputer and Embedded System Application,2011,11(6):16-19.
[7]蘇纓墩,鐘漢如.嵌入式Linux中的Nand Flash驅(qū)動(dòng)詳解[J].工業(yè)儀表與自動(dòng)化裝置,2011(4):56-60.SU Ying-dun,ZHONG Han-ru.A detail explication on Nand Flash drive in embedded Linux[J].Industrial Instrumentation&Automation,2011(4):56-60.