最近在用米爾的Z-TURN BOARD單板做小項目。順便也加強學(xué)習(xí)I2C驅(qū)動,記一篇做記錄。
I2C總線知識非常簡單,SDA,SCL,他們的時序規(guī)則是:I2C總線是由數(shù)據(jù)線SDA和時鐘SCL構(gòu)成的串行總線,可發(fā)送和接收數(shù)據(jù)。在CPU與被控IC之間、IC與IC之間進(jìn)行雙向傳送,最高傳送速率100kbps。各種被控制電路均并聯(lián)在這條總線上,但就像電話機一樣只有撥通各自的號碼才能工作,所以每個電路和模塊都有唯一的地址,在信息的傳輸過程中,I2C總線上并接的每一模塊電路既是主控器(或被控器),又是發(fā)送器(或接收器),這取決于它所要完成的功能。CPU發(fā)出的控制信號分為地址碼和控制量兩部分,地址碼用來選址,即接通需要控制的電路,確定控制的種類;控制量決定該調(diào)整的類別(如對比度、亮度等)及需要調(diào)整的量。這樣,各控制電路雖然掛在同一條總線上,卻彼此獨立,互不相關(guān)。
創(chuàng)新互聯(lián)公司是一家專業(yè)的成都網(wǎng)站建設(shè)公司,我們專注網(wǎng)站設(shè)計制作、成都網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷、企業(yè)網(wǎng)站建設(shè),買友情鏈接,廣告投放為企業(yè)客戶提供一站式建站解決方案,能帶給客戶新的互聯(lián)網(wǎng)理念。從網(wǎng)站結(jié)構(gòu)的規(guī)劃UI設(shè)計到用戶體驗提高,創(chuàng)新互聯(lián)力求做到盡善盡美。
I2C總線在傳送數(shù)據(jù)過程中共有三種類型信號, 它們分別是:開始信號、結(jié)束信號和應(yīng)答信號。
開始信號:SCL為高電平時,SDA由高電平向低電平跳變,開始傳送數(shù)據(jù)。
結(jié)束信號:SCL為高電平時,SDA由低電平向高電平跳變,結(jié)束傳送數(shù)據(jù)。
應(yīng)答信號:接收數(shù)據(jù)的IC在接收到8bit數(shù)據(jù)后,向發(fā)送數(shù)據(jù)的IC發(fā)出特定的低電平脈沖,表示已收到數(shù)據(jù)。CPU向受控單元發(fā)出一個信號后,等待受控單元發(fā)出一個應(yīng)答信號,CPU接收到應(yīng)答信號后,根據(jù)實際情況作出是否繼續(xù)傳遞信號的判斷。若未收到應(yīng)答信號,由判斷為受控單元出現(xiàn)故障。
在LINUX系統(tǒng)初始化的過程中,通過 i2c_register_board_info,將所需要的I2C從設(shè)備加入一個名為_i2c_board_list雙向循環(huán)鏈表,系統(tǒng)在成功加載I2C主設(shè)備adapt后,就會對這張鏈表里所有I2C從設(shè)備逐一地完成 i2c_client的注冊。
也就是說,i2c_client和i2c_adapter都是由i2c_core來維護(hù)的。
在xilinx-linux中,i2c從設(shè)備是通過dts文件傳遞給內(nèi)核的,內(nèi)核通過zynq_init_machine函數(shù)注冊所有的i2c從設(shè)備,i2c_client.
I2C的linux必須知道4個結(jié)構(gòu)體:i2c_adapter,i2c_algorithm,i2c_client,i2c_driver
struct i2c_adapter {
struct module owner;
unsigned int class; / classes to allow probing for /
const struct i2c_algorithm algo; / the algorithm to access the bus /
void *algo_data;
/ data fields that are valid for all devices /
struct rt_mutex bus_lock;
int timeout; / in jIFfies /
int retries;
struct device dev; / the adapter device /
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
};
i2c總線控制器數(shù)據(jù)依附于algo_data,比如xi2cps,s3c24xx_i2c。
struct device dev;成員表明i2c_adapter是一個硬件,對應(yīng)SoC上的I2C控制器。而i2c_algorithm則是這個I2C控制器的底層驅(qū)動程序。
同理:
struct i2c_client {
unsigned short flags; / div., see below /
unsigned short addr; / chip address - NOTE: 7bit /
/ addresses are stored in the /
/ LOWER 7 bits /
char name[I2C_NAME_SIZE];
struct i2c_adapter adapter; / the adapter we sit on /
struct i2c_driver driver; / and our access routines /
struct device dev; / the device structure /
int irq; / irq issued by device /
struct list_head detected;
};
struct i2c_client代表一個掛載到i2c總線上的i2c從設(shè)備,該設(shè)備所需要的數(shù)據(jù)結(jié)構(gòu),其中包括
該i2c從設(shè)備所依附的i2c主設(shè)備 struct i2c_adapter adapter
該i2c從設(shè)備的驅(qū)動程序struct i2c_driver driver
作為i2c從設(shè)備所通用的成員變量,比如addr, name等
該i2c從設(shè)備驅(qū)動所特有的數(shù)據(jù),依附于dev->driver_data下,在i2c_driver中的probe函數(shù)中設(shè)置這個結(jié)構(gòu)體成員。比如eeprom的eeprom_data。
所有i2c從設(shè)備組成的雙向鏈表:detected
struct device dev表明struct i2c_client代表的是一個硬件,比如eeprom芯片,或則rtc芯片,通過i2c總線連接到i2c_adapter硬件上。
而i2c_driver則是這個i2c_client芯片硬件的驅(qū)動程序。
我們一般會對每個I2C字符設(shè)備定義一個私有信息結(jié)構(gòu)體,而i2c_client一般被包含在這個私有信息結(jié)構(gòu)體中??催^LDR3源代碼的hacker應(yīng)該比較清楚。
i2c_client依附于i2c_adapter,也就是I2C設(shè)備和I2C總線控制器的對應(yīng)關(guān)系,一個i2c_adapter可以掛接多個i2c_client,i2c_adapter的struct list_head userspace_clients;結(jié)構(gòu)成員就是所有client的鏈表。
linux的最新版本基本上支持目前所有的I2C適配器硬件和I2C從設(shè)備,但是對于工程師來說,可能要面臨各種情況:為i2c_adapter和i2c_client編寫驅(qū)動程序。
二、I2C核心
I2C核心是源碼位于drivers/i2c/i2c-core.c,它并不依賴于硬件平臺的接口函數(shù),是I2C總線驅(qū)動和設(shè)備驅(qū)動的紐帶。
增加/刪除i2c_adapter
int i2c_add_adapter(struct i2c_adapter adapter) //調(diào)用i2c_register_adapter()
int i2c_del_adapter(struct i2c_adapter adapter)
增加/刪除i2c_driver
int i2c_register_driver(struct module owner, struct i2c_driver driver)
int i2c_add_driver(struct i2c_driver driver) //調(diào)用i2c_register_driver
void i2c_del_driver(struct i2c_driver driver)
增加/刪除i2c_client
struct i2c_client i2c_new_device(struct i2c_adapter adap, struct i2c_board_info const info)
void i2c_unregister_device(struct i2c_client client)
注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函數(shù)。之后attach被merge到了i2c_new_device中,而detach直接被unregister取代。實際上這兩個函數(shù)內(nèi)部都是調(diào)用了device_register()和device_unregister()
I2C傳輸、發(fā)送接收
int i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num)
int i2c_master_send(struct i2c_client client,const char buf ,int count)
int i2c_master_recv(struct i2c_client client, char buf ,int count)
i2c_transfer()函數(shù)用于進(jìn)行I2C 適配器和I2C 設(shè)備之間的一組消息交互,i2c_master_send()函數(shù)和i2c_master_recv()函數(shù)內(nèi)部會調(diào)用i2c_transfer()函數(shù)分別完成一條寫消息和一條讀消息。
i2c_transfer()本身不能和硬件完成消息交互,它尋找i2c_adapter對應(yīng)的i2c_algorithm,要實現(xiàn)數(shù)據(jù)傳送就要實現(xiàn)i2c_algorithm的master_xfer(),這個函數(shù)與具體的硬件有關(guān),大部分時間由廠商完成。
i2c_transfer()通過調(diào)用__i2c_transfer()完成I2C通訊:
int __i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/ Retry automatically on arbitration loss /
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
return ret;
}
可見retries為重傳嘗試次數(shù),timeout為超時時間。
三、Linux I2C總線驅(qū)動
1、I2C適配器的加載和卸除
加載:申請硬件資源,比如IO地址,中斷號,調(diào)用i2c_add_adapter加載適配器
i2c_add_adapter中會調(diào)用i2c_register_adapter函數(shù)
static int i2c_register_adapter(struct i2c_adapter *adap)
{
device_register(&adap->dev); //完成I2C主設(shè)備adapter的注冊,即注冊object和發(fā)送uevent等
i2c_scan_static_board_info(adap); //注冊i2c_clienlt
}
static void i2c_scan_static_board_info(struct i2c_adapter adapter)
{
struct i2c_devinfo devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
“Can’t create device at 0x%02x\n”,
devinfo->board_info.addr);
}
up_read(&i2c_board_lock);
}
i2c_new_device調(diào)用device_register注冊i2c從設(shè)備。
那么,這個I2C從設(shè)備組成的雙向循環(huán)鏈表,是什么時候通過什么方式建立起來的呢?
以 /arch/ARM/mach-pxa/saar.c 為例
static void __init saar_init(void)
{
saar_init_i2c();
}
static void __init saar_init_i2c(void)
{
pxa_set_i2c_info(NULL);
i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info));
}
static struct i2c_board_info saar_i2c_info[] = {
[0] = {
.type = “da9034”,
.addr = 0x34,
.platform_data = &saar_da9034_info,
.irq = PXA_GPIO_TO_IRQ(mfp_to_gpio(MFP_PIN_GPIO83)),
},
};
/ drivers/i2c/i2c-boardinfo.c /
int __init i2c_register_board_info(int busnum, structi2c_board_info const info, unsigned len)
{
struct i2c_devinfo devinfo;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //將I2C從設(shè)備加入該鏈表中
}
標(biāo)題名稱:Zynqlinux的I2C驅(qū)動學(xué)習(xí)筆記(一)
當(dāng)前路徑:http://aaarwkj.com/article16/ihpcgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、網(wǎng)站策劃、網(wǎng)站排名、品牌網(wǎng)站制作、網(wǎng)站營銷、建站公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)