吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

I2C驅(qū)動學(xué)習(xí)看這一篇就夠了

嵌入式悅翔園 ? 來源:嵌入式悅翔園 ? 2023-06-16 11:46 ? 次閱讀

一、前言

I2C協(xié)議是在開發(fā)中使用非常頻繁的一種協(xié)議,相信大家在學(xué)習(xí)單片機(jī)的時候經(jīng)常會用到支持I2C協(xié)議的模塊,I2C 總線僅僅使用 SCL、SDA 這兩根信號線就實(shí)現(xiàn)了設(shè)備之間的數(shù)據(jù)交互,極大地簡化了對硬件資源和 PCB 板布線空間的占用。因此,I2C 總線被非常廣泛地應(yīng)用在 EEPROM、實(shí)時鐘、小型 LCD 等設(shè)備與 CPU接口中。

但是與裸機(jī)開發(fā)不同的是在 Linux 系統(tǒng)中,I2C 驅(qū)動由 3 部分組成,即 I2C 核心、I2C 總線驅(qū)動和 I2C 設(shè)備驅(qū)動。今天就從這三個部分來給大家講解一下Linux中的I2C驅(qū)動,以及我們應(yīng)該如何為我們的開發(fā)板添加一個I2C設(shè)備。

二、Linux 的 I2C 體系結(jié)構(gòu)

由上面分析可知,Linux驅(qū)動分為三部分:I2C 核心、I2C 總線驅(qū)動和 I2C 設(shè)備驅(qū)動

26b6b8b2-0bf8-11ee-962d-dac502259ad0.png

2.1 Linux I2C 核心

I2C 核心提供了 I2C 總線驅(qū)動和設(shè)備驅(qū)動的注冊、注銷方法,這部分主要是一些與硬件無關(guān)的的接口函數(shù),這部分的代碼一般不用我們普通開發(fā)者進(jìn)行開發(fā)和修改,但是理解這部分的代碼邏輯和接口還是非常必要的。

I2C 核心中的主要函數(shù)如下:

注冊/注銷適配器(adapter)
inti2c_add_adapter(structi2c_adapter*adap);
inti2c_del_adapter(structi2c_adapter*adap);

注冊/注銷I2C設(shè)備驅(qū)動程序
inti2c_register_driver(structmodule*owner,structi2c_driver*driver);
inti2c_del_driver(structi2c_driver*driver);
inlineinti2c_add_driver(structi2c_driver*driver);

創(chuàng)建并注冊一個新的I2C設(shè)備
structi2c_client*i2c_new_device(structi2c_adapter*adap,structi2c_board_infoconst*info);

I2C傳輸、發(fā)送和接收
inti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum);
inti2c_master_send(structi2c_client*client,constchar*buf,intcount);
inti2c_master_recv(structi2c_client*client,char*buf,intcount);

上邊三個函數(shù)用于實(shí)現(xiàn)與I2C設(shè)備之間的數(shù)據(jù)交換。i2c_transfer函數(shù)可以進(jìn)行復(fù)雜的多消息傳輸,而i2c_master_send和i2c_master_recv函數(shù)用于單個數(shù)據(jù)消息的發(fā)送和接收。

這些函數(shù)提供了對于I2C總線讀寫操作的基本支持,簡化了I2C設(shè)備驅(qū)動的開發(fā),有了這些接口我們就不用關(guān)注I2C協(xié)議方面的代碼了,只需要調(diào)用該接口即可完成數(shù)據(jù)的傳輸。

注意: i2c_transfer函數(shù)本身不具備驅(qū)動適配器物理硬件完成消息交互的能力,它只是尋找到 i2c_adapter 對應(yīng)的 i2c_algorithm,并使用 i2c_algorithm 的 master_xfer函數(shù)真正驅(qū)動硬件流程。

2.2 Linux I2C 適配器驅(qū)動

通過上面的介紹我們知道了I2C驅(qū)動主要分為三個部分,上面我們已經(jīng)介紹了I2C核心這一部分,現(xiàn)在我們來介紹一下I2C 適配器驅(qū)動,我們知道I2C驅(qū)動和其他的那些字符設(shè)備驅(qū)動有所不同,I2C驅(qū)動中維持著一套自己的總線。

I2C 適配器驅(qū)動是Linux內(nèi)核中的一個核心模塊,總線層負(fù)責(zé)管理所有注冊到系統(tǒng)的I2C總線適配器和設(shè)備,并提供與設(shè)備通信的API函數(shù)。它提供了一些基本的操作函數(shù),如啟動總線、停止總線、發(fā)送起始信號、發(fā)送停止信號等。但是這部分是由Linux內(nèi)核完成的,并不需要我們開發(fā)者進(jìn)行修改或添加,所以了解即可。

下面我們用一張圖來看一下上面描述的這個過程:

26f4966e-0bf8-11ee-962d-dac502259ad0.png

2.3 Linux I2C 設(shè)備驅(qū)動

I2C 設(shè)備驅(qū)動要使用 i2c_driver 和 i2c_client 數(shù)據(jù)結(jié)構(gòu)并填充其中的成員函數(shù)。 i2c_client 一般被包含在設(shè)備的私有信息結(jié)構(gòu)體 yyy_data 中,而 i2c_driver 則適合被定義為全局變量并初始化。

看到I2C設(shè)備驅(qū)動的這兩個結(jié)構(gòu)體大家是不是很熟悉了,I2C設(shè)備驅(qū)動是針對特定類型的I2C設(shè)備編寫的驅(qū)動程序。它包含了對具體設(shè)備的操作和控制邏輯,通過調(diào)用I2C總線核心驅(qū)動提供的API函數(shù)與設(shè)備進(jìn)行通信。設(shè)備驅(qū)動的主要任務(wù)包括初始化設(shè)備、讀寫數(shù)據(jù)、配置設(shè)備參數(shù)等。

因?yàn)檫@部分是針對特定類型的I2C設(shè)備編寫的驅(qū)動程序,所以這部分才是要我們開發(fā)人員來完成編寫的,我們?nèi)绻枰谧约旱拈_發(fā)板上添加一個新的I2C模塊,我們就要首先編寫I2C設(shè)備驅(qū)動這部分,這部分的編寫需要調(diào)用上面我們介紹的I2C核心和I2C總線中接口函數(shù)來完成模塊的初始化。

關(guān)于I2C設(shè)備驅(qū)動我們這里先做一個了解即可,后面會詳細(xì)介紹這部分的內(nèi)容,也是我們學(xué)習(xí)I2C驅(qū)動的重點(diǎn)內(nèi)容。

2.4 Linux I2C驅(qū)動總結(jié)

I2C總線核心驅(qū)動(I2C Core Driver):【系統(tǒng)廠編寫】I2C總線核心驅(qū)動是Linux內(nèi)核中的一個核心模塊,負(fù)責(zé)管理所有注冊到系統(tǒng)的I2C總線適配器和設(shè)備,并提供與設(shè)備通信的API函數(shù)。它提供了一些基本的操作函數(shù),如啟動總線、停止總線、發(fā)送起始信號、發(fā)送停止信號等。

I2C適配器驅(qū)動(I2C Adapter Driver):【芯片廠提供】I2C適配器驅(qū)動負(fù)責(zé)與硬件的I2C控制器進(jìn)行交互,完成硬件層面的初始化、配置和操作。它將底層硬件的特定接口與I2C總線核心驅(qū)動進(jìn)行連接,使得核心驅(qū)動能夠通過適配器驅(qū)動來訪問硬件。

I2C設(shè)備驅(qū)動(I2C Device Driver):【開發(fā)者編寫】I2C設(shè)備驅(qū)動是針對特定類型的I2C設(shè)備編寫的驅(qū)動程序。它包含了對具體設(shè)備的操作和控制邏輯,通過調(diào)用I2C總線核心驅(qū)動提供的API函數(shù)與設(shè)備進(jìn)行通信。設(shè)備驅(qū)動的主要任務(wù)包括初始化設(shè)備、讀寫數(shù)據(jù)、配置設(shè)備參數(shù)等。

三部分之間的關(guān)系如下:

I2C核心層驅(qū)動作為頂層驅(qū)動,管理整個I2C子系統(tǒng),并提供了基本的I2C操作接口。

I2C適配器驅(qū)動負(fù)責(zé)與底層硬件的I2C控制器進(jìn)行交互,通過適配器驅(qū)動,I2C總線核心驅(qū)動能夠與硬件進(jìn)行通信。

I2C設(shè)備驅(qū)動則針對具體的I2C設(shè)備編寫,實(shí)現(xiàn)了對設(shè)備的初始化、讀寫數(shù)據(jù)等操作。

27044c62-0bf8-11ee-962d-dac502259ad0.png

三、具體設(shè)備驅(qū)動分析

由于作為開發(fā)者我們需要關(guān)注并且需要我們親自編寫的部分就只有設(shè)備驅(qū)動了,所以我們今天就詳細(xì)介紹一下設(shè)備驅(qū)動這部分。

當(dāng)我們需要編寫具體的I2C設(shè)備驅(qū)動程序時,我們需要編寫以下內(nèi)容:**probe函數(shù)、remove函數(shù)、操作函數(shù)以及數(shù)據(jù)傳輸與處理**,下面將對每部分進(jìn)行詳細(xì)介紹。

3.1 Probe函數(shù)

具體設(shè)備中的probe函數(shù)是I2C設(shè)備驅(qū)動中最重要的函數(shù)之一,用于在I2C設(shè)備與驅(qū)動匹配成功后進(jìn)行初始化和注冊設(shè)備。在probe函數(shù)中,可以執(zhí)行以下任務(wù):

進(jìn)行設(shè)備的特定初始化操作,例如配置設(shè)備寄存器、申請內(nèi)存資源等。

注冊字符設(shè)備、輸入設(shè)備或其他設(shè)備類別,使系統(tǒng)能夠識別和使用該設(shè)備。

存儲設(shè)備私有數(shù)據(jù),通常使用i2c_set_clientdata函數(shù)將私有數(shù)據(jù)與i2c_client相關(guān)聯(lián),方便后續(xù)的操作函數(shù)訪問。

我們在學(xué)習(xí)其他設(shè)備驅(qū)動的時候就知道了probe函數(shù)是設(shè)備與驅(qū)動匹配成功后被調(diào)用執(zhí)行的。它的原型通常如下所示:

staticinti2c_device_probe(structi2c_client*client,conststructi2c_device_id*id);

下面我們就找一個設(shè)備驅(qū)動來分析一下我們應(yīng)該如何編寫:

這里以rk3x_i2c_probe為例給大家進(jìn)行分析:
staticintrk3x_i2c_probe(structplatform_device*pdev)
{
structdevice_node*np=pdev->dev.of_node;
conststructof_device_id*match;
structrk3x_i2c*i2c;
structresource*mem;
intret=0;
intbus_nr;
u32value;
intirq;
unsignedlongclk_rate;

i2c=devm_kzalloc(&pdev->dev,sizeof(structrk3x_i2c),GFP_KERNEL);
if(!i2c)
return-ENOMEM;

match=of_match_node(rk3x_i2c_match,np);
i2c->soc_data=(structrk3x_i2c_soc_data*)match->data;

/*usecommoninterfacetogetI2Ctimingproperties*/
i2c_parse_fw_timings(&pdev->dev,&i2c->t,true);

strlcpy(i2c->adap.name,"rk3x-i2c",sizeof(i2c->adap.name));
i2c->adap.owner=THIS_MODULE;
i2c->adap.algo=&rk3x_i2c_algorithm;
i2c->adap.retries=3;
i2c->adap.dev.of_node=np;
i2c->adap.algo_data=i2c;
i2c->adap.dev.parent=&pdev->dev;

i2c->dev=&pdev->dev;

spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);

i2c->i2c_restart_nb.notifier_call=rk3x_i2c_restart_notify;
i2c->i2c_restart_nb.priority=128;
ret=register_i2c_restart_handler(&i2c->i2c_restart_nb);
if(ret){
dev_err(&pdev->dev,"failedtosetupi2crestarthandler.
");
returnret;
}

mem=platform_get_resource(pdev,IORESOURCE_MEM,0);
i2c->regs=devm_ioremap_resource(&pdev->dev,mem);
if(IS_ERR(i2c->regs))
returnPTR_ERR(i2c->regs);

/*TrytosettheI2Cadapternumberfromdt*/
bus_nr=of_alias_get_id(np,"i2c");

/*
*SwitchtonewinterfaceiftheSoCalsoofferstheoldone.
*ThecontrolbitislocatedintheGRFregisterspace.
*/
if(i2c->soc_data->grf_offset>=0){
structregmap*grf;

grf=syscon_regmap_lookup_by_phandle(np,"rockchip,grf");
if(IS_ERR(grf)){
dev_err(&pdev->dev,
"rk3x-i2cneeds'rockchip,grf'property
");
returnPTR_ERR(grf);
}

if(bus_nrdev,"rk3x-i2cneedsi2cXalias");
return-EINVAL;
}

/*27+i:writemask,11+i:value*/
value=BIT(27+bus_nr)|BIT(11+bus_nr);

ret=regmap_write(grf,i2c->soc_data->grf_offset,value);
if(ret!=0){
dev_err(i2c->dev,"CouldnotwritetoGRF:%d
",ret);
returnret;
}
}

/*IRQsetup*/
irq=platform_get_irq(pdev,0);
if(irqdev,"cannotfindrk3xIRQ
");
returnirq;
}

ret=devm_request_irq(&pdev->dev,irq,rk3x_i2c_irq,
0,dev_name(&pdev->dev),i2c);
if(retdev,"cannotrequestIRQ
");
returnret;
}

platform_set_drvdata(pdev,i2c);

if(i2c->soc_data->calc_timings==rk3x_i2c_v0_calc_timings){
/*Onlyoneclocktouseforbusclockandperipheralclock*/
i2c->clk=devm_clk_get(&pdev->dev,NULL);
i2c->pclk=i2c->clk;
}else{
i2c->clk=devm_clk_get(&pdev->dev,"i2c");
i2c->pclk=devm_clk_get(&pdev->dev,"pclk");
}

if(IS_ERR(i2c->clk)){
ret=PTR_ERR(i2c->clk);
if(ret!=-EPROBE_DEFER)
dev_err(&pdev->dev,"Can'tgetbusclk:%d
",ret);
returnret;
}
if(IS_ERR(i2c->pclk)){
ret=PTR_ERR(i2c->pclk);
if(ret!=-EPROBE_DEFER)
dev_err(&pdev->dev,"Can'tgetperiphclk:%d
",ret);
returnret;
}

ret=clk_prepare(i2c->clk);
if(retdev,"Can'tpreparebusclk:%d
",ret);
returnret;
}
ret=clk_prepare(i2c->pclk);
if(retdev,"Can'tprepareperiphclock:%d
",ret);
gotoerr_clk;
}

i2c->clk_rate_nb.notifier_call=rk3x_i2c_clk_notifier_cb;
ret=clk_notifier_register(i2c->clk,&i2c->clk_rate_nb);
if(ret!=0){
dev_err(&pdev->dev,"Unabletoregisterclocknotifier
");
gotoerr_pclk;
}

clk_rate=clk_get_rate(i2c->clk);
rk3x_i2c_adapt_div(i2c,clk_rate);

ret=i2c_add_adapter(&i2c->adap);
if(retdev,"Couldnotregisteradapter
");
gotoerr_clk_notifier;
}

dev_info(&pdev->dev,"InitializedRK3xxxI2Cbusat%p
",i2c->regs);

return0;

err_clk_notifier:
clk_notifier_unregister(i2c->clk,&i2c->clk_rate_nb);
err_pclk:
clk_unprepare(i2c->pclk);
err_clk:
clk_unprepare(i2c->clk);
returnret;
}

從上面的代碼我們可以發(fā)現(xiàn)rk3x_i2c_probe主要做了以下幾件事情:

1、通過devm_kzalloc函數(shù)為rk3x_i2c結(jié)構(gòu)體分配內(nèi)存空間;
2、從設(shè)備樹中獲取I2C設(shè)備信息并填充rk3x_i2c結(jié)構(gòu)體;
3、使用devm_platform_ioremap_resource函數(shù)來映射設(shè)備的寄存器資源到內(nèi)存中;
4、獲取并配置中斷;
5、使用i2c_add_adapter注冊設(shè)備

基本上這個驅(qū)動就是一個比較完整的I2C設(shè)備初始化流程了,我們?nèi)绻胍帉懫渌O(shè)備的驅(qū)動可以參考該驅(qū)動初始化來進(jìn)行編寫。

3.2 讀寫函數(shù)

由于rk3x_i2c中的讀寫函數(shù)和該設(shè)備關(guān)聯(lián)性較大,不具備通用性,這里以sx1_i2c_write_byte和sx1_i2c_read_byte來給大家進(jìn)行分析,該函數(shù)更具有通用性。

/*WritetoI2Cdevice*/
intsx1_i2c_write_byte(u8devaddr,u8regoffset,u8value)
{
structi2c_adapter*adap;
interr;
structi2c_msgmsg[1];
unsignedchardata[2];

adap=i2c_get_adapter(0);
if(!adap)
return-ENODEV;
msg->addr=devaddr;/*I2Caddressofchip*/
msg->flags=0;
msg->len=2;
msg->buf=data;
data[0]=regoffset;/*registernum*/
data[1]=value;/*registerdata*/
err=i2c_transfer(adap,msg,1);
i2c_put_adapter(adap);
if(err>=0)
return0;
returnerr;
}

/*ReadfromI2Cdevice*/
intsx1_i2c_read_byte(u8devaddr,u8regoffset,u8*value)
{
structi2c_adapter*adap;
interr;
structi2c_msgmsg[1];
unsignedchardata[2];

adap=i2c_get_adapter(0);
if(!adap)
return-ENODEV;

msg->addr=devaddr;/*I2Caddressofchip*/
msg->flags=0;
msg->len=1;
msg->buf=data;
data[0]=regoffset;/*registernum*/
err=i2c_transfer(adap,msg,1);

msg->addr=devaddr;/*I2Caddress*/
msg->flags=I2C_M_RD;
msg->len=1;
msg->buf=data;
err=i2c_transfer(adap,msg,1);
*value=data[0];
i2c_put_adapter(adap);

if(err>=0)
return0;
returnerr;
}

從上面的代碼可以看出,sx1_i2c_write_byte主要完成了以下功能:

1、通過調(diào)用i2c_get_adapter(0)函數(shù)獲取指定索引的I2C適配器對象并賦值給adap變量。
2、初始化一個structi2c_msg類型的數(shù)組msg,該數(shù)組包含一個元素用于I2C消息的傳輸。
3、設(shè)置msg結(jié)構(gòu)體中的字段:
addr:設(shè)備的I2C地址。
flags:傳輸標(biāo)志位,此處為0表示寫操作。
len:要傳輸?shù)淖止?jié)數(shù),此處設(shè)置為2,即寄存器地址和寄存器數(shù)據(jù)兩個字節(jié)。
buf:數(shù)據(jù)緩沖區(qū)的指針,用于存儲要發(fā)送的數(shù)據(jù)。
4、將要寫入的設(shè)備寄存器地址和數(shù)據(jù)分別存儲在data數(shù)組的第一個和第二個元素中,即data[0]=regoffset;和data[1]=value;。
5、調(diào)用i2c_transfer()函數(shù)進(jìn)行I2C消息傳輸,將數(shù)據(jù)寫入設(shè)備寄存器。
6、使用i2c_put_adapter()函數(shù)釋放先前獲取的I2C適配器對象。

sx1_i2c_read_byte主要完成了以下功能:

1、通過調(diào)用i2c_get_adapter(0)函數(shù)獲取指定索引的I2C適配器對象并賦值給adap變量。
2、初始化一個structi2c_msg類型的數(shù)組msg,該數(shù)組包含一個元素用于I2C消息的傳輸。
3、設(shè)置msg結(jié)構(gòu)體中的字段:
addr:設(shè)備的I2C地址。
flags:傳輸標(biāo)志位,此處為0表示寫操作。
len:要傳輸或接收的字節(jié)數(shù)。
buf:數(shù)據(jù)緩沖區(qū)的指針,用于存儲要發(fā)送或接收的數(shù)據(jù)。
4、將要讀取的設(shè)備寄存器地址存儲在data數(shù)組的第一個元素中,即data[0]=regoffset;。
5、調(diào)用i2c_transfer()函數(shù)進(jìn)行I2C消息傳輸,將數(shù)據(jù)寫入設(shè)備寄存器。
6、更改flags字段為I2C_M_RD,表示接收模式(讀操作)。
7、再次調(diào)用i2c_transfer()函數(shù)進(jìn)行I2C消息傳輸,從設(shè)備中讀取數(shù)據(jù)。
8、將讀取到的數(shù)據(jù)存儲在data數(shù)組的第一個元素中,即*value=data[0];。
9、使用i2c_put_adapter()函數(shù)釋放先前獲取的I2C適配器對象。

對比I2C讀和寫的過程大家可能會發(fā)現(xiàn)I2C讀的過程為什么調(diào)用了兩次i2c_transfer函數(shù)呢?多調(diào)用了一次i2c_transfer函數(shù)是因?yàn)槲覀冊谡{(diào)用i2c_transfer讀取數(shù)據(jù)時,需要先發(fā)送要讀取的寄存器地址給設(shè)備,然后再從設(shè)備讀取實(shí)際的數(shù)據(jù)。所以第一次使用i2c_transfer發(fā)送的信息為需要讀取的地址信息,第二次將標(biāo)志位改為讀,然后使用i2c_transfer將從設(shè)備返回的信息存儲到i2c_adapter中。

四、I2C驅(qū)動中幾個重要的結(jié)構(gòu)體

在I2C驅(qū)動中,有三個比較重要的結(jié)構(gòu)體用于描述和管理I2C設(shè)備和傳輸操作。下面就這三個結(jié)構(gòu)體的成員以及作用來給大家講解一下:

4.1 i2c_adapter 結(jié)構(gòu)體

定義位置:i2c.h結(jié)構(gòu)體原型:

structi2c_adapter{
structmodule*owner;
unsignedintclass;/*classestoallowprobingfor*/
conststructi2c_algorithm*algo;/*thealgorithmtoaccessthebus*/
void*algo_data;

/*datafieldsthatarevalidforalldevices*/
conststructi2c_lock_operations*lock_ops;
structrt_mutexbus_lock;
structrt_mutexmux_lock;

inttimeout;/*injiffies*/
intretries;
structdevicedev;/*theadapterdevice*/
unsignedlonglocked_flags;/*ownedbytheI2Ccore*/
#defineI2C_ALF_IS_SUSPENDED0
#defineI2C_ALF_SUSPEND_REPORTED1

intnr;
charname[48];
structcompletiondev_released;

structmutexuserspace_clients_lock;
structlist_headuserspace_clients;

structi2c_bus_recovery_info*bus_recovery_info;
conststructi2c_adapter_quirks*quirks;

structirq_domain*host_notify_domain;
structregulator*bus_regulator;
};

幾個重要的成員:

name:適配器的名稱。
nr:適配器的編號。
bus_lock和bus_unlock:用于保護(hù)對適配器的并發(fā)訪問的鎖機(jī)制。
algo:指向I2C算法結(jié)構(gòu)體的指針,包含了適配器的通信算法,如標(biāo)準(zhǔn)模式、快速模式、高速模式等。

4.2 i2c_client 結(jié)構(gòu)體

定義位置:i2c.h結(jié)構(gòu)體原型:

structi2c_client{
unsignedshortflags;/*div.,seebelow*/
#defineI2C_CLIENT_PEC0x04/*UsePacketErrorChecking*/
#defineI2C_CLIENT_TEN0x10/*wehaveatenbitchipaddress*/
/*MustequalI2C_M_TENbelow*/
#defineI2C_CLIENT_SLAVE0x20/*wearetheslave*/
#defineI2C_CLIENT_HOST_NOTIFY0x40/*WewanttouseI2Chostnotify*/
#defineI2C_CLIENT_WAKE0x80/*forboard_info;trueiffcanwake*/
#defineI2C_CLIENT_SCCB0x9000/*UseOmnivisionSCCBprotocol*/
/*MustmatchI2C_M_STOP|IGNORE_NAK*/

unsignedshortaddr;/*chipaddress-NOTE:7bit*/
/*addressesarestoredinthe*/
/*_LOWER_7bits*/
charname[I2C_NAME_SIZE];
structi2c_adapter*adapter;/*theadapterwesiton*/
structdevicedev;/*thedevicestructure*/
intinit_irq;/*irqsetatinitialization*/
intirq;/*irqissuedbydevice*/
structlist_headdetected;
#ifIS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_tslave_cb;/*callbackforslavemode*/
#endif
void*devres_group_id;/*IDofprobedevresgroup*/
};

幾個重要的成員:

flags:標(biāo)志位,用于指定設(shè)備的特性和行為。
addr:設(shè)備的I2C地址。
adapter:指向i2c_adapter的指針,表示所屬的I2C適配器。
driver:指向設(shè)備驅(qū)動程序的指針,表示設(shè)備所使用的驅(qū)動。

4.3 i2c_driver 結(jié)構(gòu)體

定義位置:i2c.h結(jié)構(gòu)體原型:

structi2c_driver{
unsignedintclass;

union{
/*Standarddrivermodelinterfaces*/
int(*probe)(structi2c_client*client);
/*
*Legacycallbackthatwaspartofaconversionof.probe().
*Todayithasthesamesemanticas.probe().Don'tusefornew
*code.
*/
int(*probe_new)(structi2c_client*client);
};
void(*remove)(structi2c_client*client);


/*drivermodelinterfacesthatdon'trelatetoenumeration*/
void(*shutdown)(structi2c_client*client);

/*Alertcallback,forexamplefortheSMBusalertprotocol.
*Theformatandmeaningofthedatavaluedependsontheprotocol.
*FortheSMBusalertprotocol,thereisasinglebitofdatapassed
*asthealertresponse'slowbit("eventflag").
*FortheSMBusHostNotifyprotocol,thedatacorrespondstothe
*16-bitpayloaddatareportedbytheslavedeviceactingasmaster.
*/
void(*alert)(structi2c_client*client,enumi2c_alert_protocolprotocol,
unsignedintdata);

/*aioctllikecommandthatcanbeusedtoperformspecificfunctions
*withthedevice.
*/
int(*command)(structi2c_client*client,unsignedintcmd,void*arg);

structdevice_driverdriver;
conststructi2c_device_id*id_table;

/*Devicedetectioncallbackforautomaticdevicecreation*/
int(*detect)(structi2c_client*client,structi2c_board_info*info);
constunsignedshort*address_list;
structlist_headclients;

u32flags;
};

幾個重要的成員:

driver:是一個structdevice_driver結(jié)構(gòu)體,用于向Linux設(shè)備模型注冊驅(qū)動程序。
probe和remove:指向探測和移除設(shè)備的函數(shù)指針,通過這兩個函數(shù),驅(qū)動程序可以在發(fā)現(xiàn)匹配的設(shè)備時執(zhí)行初始化操作,并在設(shè)備被移除時執(zhí)行清理操作。
id_table:用于指定驅(qū)動程序支持的I2C設(shè)備ID列表,以便匹配對應(yīng)的設(shè)備。

這些結(jié)構(gòu)體共同構(gòu)成了Linux內(nèi)核中的I2C驅(qū)動框架,提供了對I2C總線、適配器和設(shè)備的抽象和管理功能。開發(fā)者可以基于這些結(jié)構(gòu)體來編寫自己的I2C驅(qū)動程序,并實(shí)現(xiàn)與I2C設(shè)備的通信和控制。所以我們的工作就是填充這些結(jié)構(gòu)體然后調(diào)用對應(yīng)的接口把我們填充好的結(jié)構(gòu)體傳遞給I2C設(shè)備器驅(qū)動和核心驅(qū)動從而完成設(shè)備的初始化和讀寫操作。

五、總結(jié)

I2C驅(qū)動的學(xué)習(xí)有一個特點(diǎn):弄懂比較難,會用比較簡單,這是因?yàn)橛泻芏嗟挠须y度的內(nèi)容以及和協(xié)議相關(guān)的內(nèi)容都已經(jīng)被Linux或者芯片廠封裝好了,我們需要做的就是使用他們提供的這些接口完成指定設(shè)備的讀寫操作,但是我們的學(xué)習(xí)不能止步于此,所以我們不但要會用,還要知其然知其所以然。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 驅(qū)動器
    +關(guān)注

    關(guān)注

    53

    文章

    8271

    瀏覽量

    147055
  • SDA
    SDA
    +關(guān)注

    關(guān)注

    0

    文章

    124

    瀏覽量

    28259
  • I2C協(xié)議
    +關(guān)注

    關(guān)注

    0

    文章

    26

    瀏覽量

    8526
  • Linux驅(qū)動
    +關(guān)注

    關(guān)注

    0

    文章

    43

    瀏覽量

    10014

原文標(biāo)題:Linux驅(qū)動:I2C驅(qū)動學(xué)習(xí)看這一篇就夠了

文章出處:【微信號:嵌入式悅翔園,微信公眾號:嵌入式悅翔園】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    GPIO模擬I2C總線的驅(qū)動設(shè)計(jì)與實(shí)現(xiàn)

    I2C總線簡單方便,是我們經(jīng)常使用的種總線。但有時候我們的MCU沒有足夠多的I2C控制器來實(shí)現(xiàn)我們的應(yīng)用,所幸我可以使用普通的GPIO引腳來模擬低速的I2C總線通信。
    發(fā)表于 12-14 14:19 ?5448次閱讀
    GPIO模擬<b class='flag-5'>I2C</b>總線的<b class='flag-5'>驅(qū)動</b>設(shè)計(jì)與實(shí)現(xiàn)

    I2C總線驅(qū)動程序的實(shí)現(xiàn)

    I2C總線驅(qū)動程序的實(shí)現(xiàn) I2C 驅(qū)動程序的簡介本驅(qū)動程序?yàn)闃?biāo)準(zhǔn)的51 系列CPU 編寫,讓CPU 模擬成
    發(fā)表于 09-26 17:25 ?5777次閱讀
    <b class='flag-5'>I2C</b>總線<b class='flag-5'>驅(qū)動</b>程序的實(shí)現(xiàn)

    基于51的I2c總線

    I2c總線,基于51的I2c總線,程序學(xué)習(xí)。快來下載學(xué)習(xí)
    發(fā)表于 01-13 11:49 ?34次下載

    I2C LCD 器件通過驅(qū)動帶有 I2C 接口的 2 線式 16 字符 LCD

    I2C LCD 器件通過驅(qū)動帶有 I2C 接口的 2 線式 16 字符 LCD
    發(fā)表于 10-10 08:22 ?13次下載
    <b class='flag-5'>I2C</b> LCD 器件通過<b class='flag-5'>驅(qū)動</b>帶有 <b class='flag-5'>I2C</b> 接口的 <b class='flag-5'>2</b> 線式 16 字符 LCD

    Linux的I2C驅(qū)動架構(gòu)

    控制器的差異,不考慮其實(shí)現(xiàn)細(xì)節(jié)地與硬件設(shè)備通訊。1.1 總線驅(qū)動在系統(tǒng)開機(jī)時,首先裝載的是I2C總線驅(qū)動個總線驅(qū)動用于支持
    發(fā)表于 04-02 14:38 ?698次閱讀

    I2C通信協(xié)議應(yīng)該如何學(xué)習(xí)

    我最近剛做完I2C通信協(xié)議的編寫與調(diào)試,下面介紹下我從開始理解夏老師的程序,修改程序,直到下板調(diào)試整個的學(xué)習(xí)過程,希望對大家學(xué)習(xí)
    發(fā)表于 04-28 08:00 ?22次下載
    <b class='flag-5'>I2C</b>通信協(xié)議應(yīng)該如何<b class='flag-5'>學(xué)習(xí)</b>

    I2C系列的合集,可以系統(tǒng)學(xué)習(xí)I2C協(xié)議

    這篇文章給大家?guī)砹?b class='flag-5'>I2C系列的合集,可以系統(tǒng)學(xué)習(xí)I2C協(xié)議。大家趕緊看看吧! 1、I2C總線:何時使用I2C緩沖器 本文討論了使用
    的頭像 發(fā)表于 09-23 15:28 ?3192次閱讀

    STM32學(xué)習(xí)I2C協(xié)議(讀寫EEPROM)

    關(guān)于STM32學(xué)習(xí)分享第七章 I2C協(xié)議(讀寫EEPROM)文章目錄關(guān)于STM32學(xué)習(xí)分享前言二、代碼1.i2c.c2.i2c.h3.main.c總結(jié)前言開始!開始!單片機(jī)的
    發(fā)表于 11-30 15:21 ?32次下載
    STM32<b class='flag-5'>學(xué)習(xí)</b>之<b class='flag-5'>I2C</b>協(xié)議(讀寫EEPROM)

    Linux驅(qū)動I2C設(shè)備驅(qū)動(基于Freescale i.MX6ULL平臺了解I2C驅(qū)動框架,順便寫個簡陋的MPU6050驅(qū)動

    文章目錄1、簡介2I2C總線、設(shè)備和驅(qū)動的結(jié)構(gòu)體定義2.1 結(jié)構(gòu)體定義--I2C總線2.2 結(jié)構(gòu)體定義--I2C設(shè)備2.3 結(jié)構(gòu)體定義--
    發(fā)表于 12-06 13:51 ?8次下載
    Linux<b class='flag-5'>驅(qū)動</b>:<b class='flag-5'>I2C</b>設(shè)備<b class='flag-5'>驅(qū)動</b>(基于Freescale <b class='flag-5'>i</b>.MX6ULL平臺了解<b class='flag-5'>I2C</b>的<b class='flag-5'>驅(qū)動</b>框架,順便寫個簡陋的MPU6050<b class='flag-5'>驅(qū)動</b>)

    嵌入式內(nèi)核及驅(qū)動開發(fā)-09IIC子系統(tǒng)框架使用(I2C協(xié)議和時序,I2C驅(qū)動框架,I2C從設(shè)備驅(qū)動開發(fā),MPU6050硬件連接

    文章目錄I2c協(xié)議和時序I2c介紹I2c硬件連接I2c總線的信號I2c總線寫時序I2c總線讀時序
    發(fā)表于 12-06 14:06 ?17次下載
    嵌入式內(nèi)核及<b class='flag-5'>驅(qū)動</b>開發(fā)-09IIC子系統(tǒng)框架使用(<b class='flag-5'>I2C</b>協(xié)議和時序,<b class='flag-5'>I2C</b><b class='flag-5'>驅(qū)動</b>框架,<b class='flag-5'>I2C</b>從設(shè)備<b class='flag-5'>驅(qū)動</b>開發(fā),MPU6050硬件連接

    硬件I2C與模擬I2C

    硬件I2C對應(yīng)芯片上的I2C外設(shè),有相應(yīng)I2C驅(qū)動電路,其所使用的I2C管腳也是專用的,因而效率要遠(yuǎn)高于軟件模擬的
    發(fā)表于 12-28 19:14 ?81次下載
    硬件<b class='flag-5'>I2C</b>與模擬<b class='flag-5'>I2C</b>

    ESP32 之 ESP-IDF 教學(xué)(六)——I2C數(shù)據(jù)總線(I2C

    ESP32 之 ESP-IDF 學(xué)習(xí)筆記(六)【I2C數(shù)據(jù)總線(I2C)】文章目錄ESP32 之 ESP-IDF 學(xué)習(xí)筆記(六)【
    發(fā)表于 12-28 19:25 ?22次下載
    ESP32 之 ESP-IDF 教學(xué)(六)——<b class='flag-5'>I2C</b>數(shù)據(jù)總線(<b class='flag-5'>I</b>2<b class='flag-5'>C</b>)

    Linux I2C驅(qū)動入門知識科普

    I2C 總線驅(qū)動I2C總線驅(qū)動就是SOC的 I2C控制器驅(qū)動,也叫做
    的頭像 發(fā)表于 12-29 13:59 ?1762次閱讀

    I2C控制器驅(qū)動介紹

    (控制器)抽象成 i2c_adapter。 對于I2C 適配器,肯定要對外提供讀寫 API 函數(shù),設(shè)備驅(qū)動程序可以使用這些 API 函數(shù)來完成讀寫操作。
    的頭像 發(fā)表于 07-22 15:38 ?1552次閱讀
    <b class='flag-5'>I2C</b>控制器<b class='flag-5'>驅(qū)動</b>介紹

    I2C子系統(tǒng)SW Architecture

    I2C SW Architecture 【driver 驅(qū)動層】由普通驅(qū)動工程師負(fù)責(zé),【i2c 核心層】由 Linux 提供,【i2c 核心
    的頭像 發(fā)表于 07-22 16:01 ?1002次閱讀
    <b class='flag-5'>I2C</b>子系統(tǒng)SW Architecture
    真人百家乐官网试玩账号 | 新加坡百家乐官网规则| 百家乐官网技巧经| 基础百家乐博牌规| 百家乐官网群号| 百家乐双峰县| 百家乐官网桌14人| 赌场百家乐的玩法技巧和规则| 沂南县| 百家乐网站那个好| 高雄市| 网络百家乐| 五大连池市| 百家乐网上漏洞| 百家乐官网三遍| 希尔顿百家乐娱乐城 | 赌场风云主题曲| 24山坐向| 网络真人赌场| 百家乐分享| 百家乐官网视频视频| 百家乐英皇娱乐平台| 来博百家乐官网现金网| 新梦想百家乐的玩法技巧和规则| 百家乐官网公式论坛| 大发888分析| 保单百家乐技巧| 百家乐官网破解辅助| 全景网百家乐的玩法技巧和规则 | 开心8| 电脑百家乐的玩法技巧和规则| 百家乐官网赌博彩| 皇冠信用网| 百家乐双龙| 百家乐官网的巧门| 网络投注| 威尼斯人娱乐城投注| 百家乐官网都是什么人玩的| 百家乐官网赢钱秘密| 大发888登陆器下载| 百家乐送1000|