RTC 實(shí)時(shí)時(shí)鐘驅(qū)動(dòng)
-------I2C軟件模擬通信
內(nèi)核版本: linux-2.4.21
文檔設(shè)計(jì): 侯輝華
版 本: 1.01
時(shí) 間: 2007/06/10
內(nèi)容簡(jiǎn)介: 介紹接在I2C總線上RTC實(shí)時(shí)時(shí)鐘設(shè)備的驅(qū)動(dòng), 使用軟件模擬的方法完成I2C的通信; 介紹了Linux下的時(shí)鐘系統(tǒng), 以及I2C的層次結(jié)構(gòu).
目錄索引:
一. Linux下的時(shí)鐘系統(tǒng)簡(jiǎn)介.
二. Linux對(duì)時(shí)間的表示.
三. Linux時(shí)鐘中斷的初始化及處理.
四. RTC設(shè)備驅(qū)動(dòng)程序.
五. I2C總線讀寫.
六. Linux下的I2C驅(qū)動(dòng)層次結(jié)構(gòu)..
一. Linux下的時(shí)鐘系統(tǒng)簡(jiǎn)介
實(shí)際上,linux系統(tǒng)有兩個(gè)時(shí)鐘:一個(gè)是由主板電池驅(qū)動(dòng)的“Real Time Clock”也叫做RTC或者叫CMOS時(shí)鐘,硬件時(shí)鐘。當(dāng)操作系統(tǒng)關(guān)機(jī)的時(shí)候,用這個(gè)來記錄時(shí)間,但是對(duì)于運(yùn)行的系統(tǒng)是不用這個(gè)時(shí)間的。另一個(gè)時(shí)間是 “System clock”也叫內(nèi)核時(shí)鐘或者軟件時(shí)鐘,是由軟件根據(jù)時(shí)間中斷來進(jìn)行計(jì)數(shù)的,內(nèi)核時(shí)鐘在系統(tǒng)關(guān)機(jī)的情況下是不存在的,所以,當(dāng)操作系統(tǒng)啟動(dòng)的時(shí)候,內(nèi)核時(shí)鐘是要讀取RTC時(shí)間來進(jìn)行時(shí)間同步.
linux的內(nèi)核時(shí)間實(shí)際上是記錄從1970年1月1日距離現(xiàn)在的秒數(shù),并且以GMT(格林尼治時(shí)間)(或者叫UTC- Coordinated Universal Time)為標(biāo)準(zhǔn), UTC是不隨著DST(夏令時(shí))變換,需要有變化的是由應(yīng)用程序自身來完成時(shí)間的轉(zhuǎn)換。
二. Linux對(duì)時(shí)間的表示
通常,操作系統(tǒng)可以使用三種方法來表示系統(tǒng)的當(dāng)前時(shí)間與日期:①最簡(jiǎn)單的一種方法就是直接用一個(gè)64位的計(jì)數(shù)器來對(duì)時(shí)鐘滴答進(jìn)行計(jì)數(shù)。②第二種方法就是用一個(gè)32位計(jì)數(shù)器來對(duì)秒進(jìn)行計(jì)數(shù),同時(shí)還用一個(gè)32位的輔助計(jì)數(shù)器對(duì)時(shí)鐘滴答計(jì)數(shù),之子累積到一秒為止。因?yàn)?/span>2的32次方超過136年,因此這種方法直至22世紀(jì)都可以讓系統(tǒng)工作得很好。③第三種方法也是按時(shí)鐘滴答進(jìn)行計(jì)數(shù),但是是相對(duì)于系統(tǒng)啟動(dòng)以來的滴答次數(shù),而不是相對(duì)于相對(duì)于某個(gè)確定的外部時(shí)刻;當(dāng)讀外部后備時(shí)鐘(如RTC)或用戶輸入實(shí)際時(shí)間時(shí),根據(jù)當(dāng)前的滴答次數(shù)計(jì)算系統(tǒng)當(dāng)前時(shí)間。
Linux通常都采用第三種方法來維護(hù)系統(tǒng)的時(shí)間與日期, 通過時(shí)鐘點(diǎn)滴進(jìn)行計(jì)時(shí)的基礎(chǔ)原理, 可以參看下面介紹的參考文檔, 主要原理是通過硬件的中斷累積來計(jì)時(shí), 但必要要設(shè)置硬件中斷一次所須的時(shí)間, 一般具體的不同的芯片都不同, EP9302系統(tǒng)具體設(shè)置如下:
1. EP93xx系列芯片有四個(gè)Timer計(jì)時(shí)器, 使用的是Timer1, 與具體芯片相關(guān)的內(nèi)容在如下兩個(gè)文件:
linux-2.4.21\arch\arm\mach-ep93xx\time.c,
linux-2.4.21\arch\arm\mach-ep93xx\time.h
2. 針對(duì)整個(gè)Arm體系的時(shí)鐘相關(guān)文件為:
linux-2.4.21\arch\arm\kernel\time.c
時(shí)鐘中斷計(jì)時(shí)的主要相關(guān)函數(shù)為如下兩個(gè):
1. ep93xx_gettimeoffset()的作用就是返回距最近一次時(shí)鐘中斷發(fā)生后, Timer已經(jīng)累積的時(shí)間(但還未滿足引發(fā)一次時(shí)鐘中斷), 這個(gè)時(shí)間值單位為微秒, 獲取當(dāng)前時(shí)間時(shí), 就是累計(jì)已經(jīng)發(fā)生的Timer中斷次數(shù)所經(jīng)歷的時(shí)間, 然后加上這個(gè)即將要發(fā)生中斷所過去的時(shí)間, 這樣取得當(dāng)前時(shí)間的精度是相當(dāng)高的.
2. LATCH的含義是指一次時(shí)鐘中斷要經(jīng)過多少個(gè)Timer時(shí)鐘周期, TIMER1LOAD寄存器設(shè)置的就是這個(gè)值,ep93xx的計(jì)時(shí)器Timer1會(huì)將這個(gè)值一直遞減直至0, 如此就引發(fā)一次時(shí)鐘中斷, 然后又重LATCH重頭開始遞減.
3. xtime記載的即為系統(tǒng)自開機(jī)以來的當(dāng)前時(shí)間, 單位為秒, 精確度為微秒. 因此在開機(jī)時(shí)必須從RTC當(dāng)中取得真實(shí)的時(shí)間來賦此初值, EP93xx系列直接初始此值為其自身所帶RTC模塊的時(shí)間值, RTCDR寄存器是EP93xx所自帶的RTC模塊的寄存器, 其值單位為秒,基準(zhǔn)為相對(duì)1970年, 此處即為我們要改動(dòng)的地方.將其值從i2c的RTC實(shí)時(shí)芯片中取回賦給它.
static unsigned longep93xx_gettimeoffset(void)
{
unsigned longhwticks;
hwticks = LATCH -(inl(TIMER1VALUE) & 0xffff);
return ((hwticks *tick) / LATCH);
}
void __init ep93xx_setup_timer(void) //初始化Timer,設(shè)定時(shí)鐘中斷周期…
{
gettimeoffset =ep93xx_gettimeoffset;
outl(0,TIMER1CONTROL);
outl(LATCH - 1,TIMER1LOAD); //設(shè)定Timer經(jīng)多少個(gè)Timer時(shí)鐘周期后產(chǎn)生中斷…
outl(0xc8,TIMER1CONTROL);
xtime.tv_sec =inl(RTCDR); //從ep93xx內(nèi)部RTC模塊讀取時(shí)間,后將重新從cmos讀.
}
以下詳細(xì)介紹一下時(shí)鐘點(diǎn)滴計(jì)時(shí)的幾個(gè)基本參數(shù), 以下定義的出處, 除非特別指出, 一般是位于各自不同的平臺(tái)的文件夾下定義:
linux-2.4.21\include\asm-arm\arch-ep93xx
linux-2.4.21\arch\arm\mach-ep93xx\
1. 時(shí)鐘周期(clock cycle)的頻率:計(jì)時(shí)器Timer晶體振蕩器在1秒時(shí)間內(nèi)產(chǎn)生的時(shí)鐘脈沖個(gè)數(shù)就是時(shí)鐘周期的頻率, 要注意這個(gè)Timer的時(shí)鐘周期頻率要與時(shí)鐘中斷的頻率區(qū)別開來, Linux用宏CLOCK_TICK_RATE來表示計(jì)時(shí)器的輸入時(shí)鐘脈沖的頻率(此值在EP93xx上是508KHZ),該宏定義在timex.h頭文件中:
#defineCLOCK_TICK_RATE 508000 /* Underlying HZ */
2. 時(shí)鐘中斷(clock tick):我們知道當(dāng)計(jì)數(shù)器減到0值時(shí),它就在IRQ0上產(chǎn)生一次時(shí)鐘中斷,也即一次時(shí)鐘中斷, 計(jì)數(shù)器的初始值決定了要過多少時(shí)鐘周期才產(chǎn)生一次時(shí)鐘中斷,因此也就決定了一次時(shí)鐘滴答的時(shí)間間隔長(zhǎng)度. 在EP93xx系統(tǒng)中, Timer1的TIMER1LOAD 的值決定過多少時(shí)鐘周期后產(chǎn)生一次時(shí)鐘中斷.
3. 時(shí)鐘中斷的頻率(HZ):也即1秒時(shí)間內(nèi)Timer所產(chǎn)生的時(shí)鐘中斷次數(shù)。確定了時(shí)鐘中斷的頻率值后也就可以確定Timer的計(jì)數(shù)器初值。Linux內(nèi)核用宏HZ來表示時(shí)鐘中斷的頻率,而且在不同的平臺(tái)上HZ有不同的定義值。對(duì)于SPARC、MIPS、ARM和i386等平臺(tái)HZ的值都是100。該宏在ARM平臺(tái)上的定義如下(param.h):
#ifndef HZ
#define HZ 100
#endif
據(jù)HZ值,可知每隔(1000ms/HZ)=10ms發(fā)生一次時(shí)鐘中斷.
4. 時(shí)鐘中斷的時(shí)間間隔:Linux用全局變量tick來表示時(shí)鐘中斷的時(shí)間間隔長(zhǎng)度,其實(shí)定義了HZ之后, 即決定了此間隔值, tick變量的單位是微妙(μs), 該變量定義在kernel/timer.c文件中,如下:
long tick =(1000000 + HZ/2) / HZ; /* timer interrupt period */.
5. 宏LATCH:Linux用宏LATCH來定義要設(shè)置到Timer中的值,它表示TImer將沒隔多少個(gè)時(shí)鐘周期產(chǎn)生一次時(shí)鐘中斷。顯然LATCH應(yīng)該由下列公式計(jì)算: LATCH=(1秒之內(nèi)的Timer時(shí)鐘周期個(gè)數(shù))÷(1秒之內(nèi)的時(shí)鐘中斷次數(shù))=(CLOCK_TICK_RATE)÷(HZ).
三. Linux時(shí)鐘中斷的初始化及處理
以下著重描述一下時(shí)鐘中斷時(shí)與RTC相關(guān)的修改:
文件:linux-2.4.21\arch\arm\kernel\time.c
描述:時(shí)鐘初始化化, 在start_kernel()當(dāng)中調(diào)用.
void __init time_init(void)
{
xtime.tv_usec= 0;
xtime.tv_sec = 0;
setup_timer();
}
文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h
描述:安裝時(shí)鐘中斷服務(wù)程序,從RTC更新初始化系統(tǒng)時(shí)鐘, 在time_init()當(dāng)中調(diào)用.
/*
* Set up timer interrupt, and return thecurrent time in seconds.
*/
static inline void setup_timer(void)
{
ep93xx_setup_timer();
//houhh 20070713...
/////////
xtime.tv_sec= get_cmos_time(); //從RTC設(shè)備中讀取當(dāng)前時(shí)間…
set_rtc= set_cmos_time; //初始化更新系統(tǒng)時(shí)間到RTC的函數(shù), 在do_set_rtc()中調(diào)用…
/////////
timer_irq.handler= ep93xx_timer_interrupt; //時(shí)鐘中斷服務(wù)程序…
setup_arm_irq(IRQ_TIMER1,&timer_irq); //安裝時(shí)鐘中斷處理…
}
文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h
描述:讀寫I2C的RTC設(shè)備,軟件模擬方式, 引自i2c-ep93xx.c, 將在下面詳細(xì)介紹.
extern uchar pcf8563_readdata(ucharaddress);
extern int pcf8563_writedata(ucharaddress, uchar mdata);
#define CMOS_READ(addr) pcf8563_readdata(addr)
#define CMOS_WRITE(data, addr) pcf8563_writedata(addr, data)
文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h
描述:設(shè)置及讀寫RTC, 主要通過宏CMOS_READ/ CMOS_WRITE完成功能.
unsigned long get_cmos_time(void)
static int set_cmos_time(unsigned longnowtime)
文件: linux-2.4.21\arch\arm\mach-ep93xx\time.h
描述:時(shí)鐘中斷服務(wù)程序,并檢測(cè)更新系統(tǒng)時(shí)鐘到RTC, 在setup_timer()當(dāng)中調(diào)用.
/*
* IRQ handler for the timer
*/
static void ep93xx_timer_interrupt(intirq, void *dev_id, struct pt_regs *regs)
{
outl(1, TIMER1CLEAR );
do_leds();
do_set_rtc(); //houhh 20070713, 檢測(cè)是否要更新系統(tǒng)當(dāng)前時(shí)間到cmos rtc設(shè)備...
do_timer(regs);
do_profile(regs);
}
一般認(rèn)為時(shí)鐘中斷計(jì)時(shí)的精度較高,所以在時(shí)鐘中斷服務(wù)程序中會(huì)每隔11分鐘(660秒)就檢測(cè)一次是否須要將此時(shí)的系統(tǒng)時(shí)間寫回到RTC當(dāng)中, 所以在ep93xx的時(shí)鐘中斷服務(wù)程序中,須要加上do_set_rtc(),關(guān)于這個(gè)函數(shù)具體的功能請(qǐng)具體參見源碼.
四. RTC設(shè)備驅(qū)動(dòng)程序
主要指出RTC設(shè)備及相關(guān)操作,這一塊相當(dāng)簡(jiǎn)單,RTC時(shí)鐘處理成一簡(jiǎn)單字符設(shè)備.
基礎(chǔ)文件結(jié)構(gòu):
static struct file_operationspcf8563_fops = {
owner:THIS_MODULE,
ioctl:pcf8563_ioctl,
open:pcf8563_open,
release:pcf8563_release,
};
文件:linux-2.4.21\drivers\char\pcf8563_rtc.c
描述: RTC設(shè)備的文件結(jié)構(gòu),指文件打開及釋放操作,其實(shí)最核心的還是ioctl,這里可以進(jìn)行時(shí)間讀取以及時(shí)間設(shè)置操作, 具體使用示例可以參考rtctest.c示例文件.
文件:linux-2.4.21\drivers\char\pcf8563_rtc.c
描述:時(shí)間設(shè)置與讀取.
get_rtc_time(struct rtc_time *tm);
Set_rtc_time(struct rtc_time *tm);
文件: linux-2.4.21\drivers\charpcf8563_rtc.c
描述: I2C讀寫, 這兩個(gè)函數(shù)是從文件i2c-ep93xx.c中引用的,定義成宏.
extern intpcf8563_writedata(uchar address, uchar mdata);
extern ucharpcf8563_readdata(uchar address);
#definertc_reg_read(x) pcf8563_readdata(x)
#definertc_reg_write(x,y) pcf8563_writedata(x,y)
文件: linux-2.4.21\drivers\charpcf8563_rtc.c
描述: 模塊初始化,負(fù)責(zé)注冊(cè)/注消RTC字符設(shè)備.
int __init pcf8563_init(void)
void __exit pcf8563_exit(void)
五. I2C總線讀寫
i2c總線讀寫方式為軟件模擬方式,因?yàn)?/span>ep93xx沒有相關(guān)的i2c總線控制器,因此只能通過軟件方式來模擬I2C總線的讀寫, 在調(diào)試過程中遇到如下問題,注意如下即可:
1. 注意延時(shí)的時(shí)間,I2C的開始條件與讀寫時(shí)都須要一定的延時(shí),根據(jù)主設(shè)備(即ep93xx cpu)的運(yùn)行速度,此延時(shí)必須是一個(gè)穩(wěn)定的時(shí)間,通常采取讀取特定外設(shè)i/o以達(dá)此目的.
2. 注意在改變I2C的SDA數(shù)據(jù)線狀態(tài)時(shí),必須是在SCL時(shí)鐘線為低的時(shí)候,因?yàn)楦鶕?jù)開始與結(jié)束條件的要求,開始與結(jié)束條件是在SCL時(shí)鐘線高的時(shí)候SDA拉高或者拉低,所以如果在傳送數(shù)據(jù)時(shí),SCL為高,則會(huì)被當(dāng)成開始或結(jié)束條件,通信失敗.
3. 在讀寫I2C總線時(shí),每傳送或接收一BYTE數(shù)據(jù),必須要進(jìn)行回應(yīng).
Ø 寫回應(yīng):主設(shè)備寫完八位數(shù)據(jù)后,在第九個(gè)周期等待從設(shè)備來拉低SDA作為回應(yīng),因此須先將SDA在第八周期SCL低時(shí)拉高,之后拉高SCL等從設(shè)備回應(yīng),等到從設(shè)備回應(yīng)后拉低SCL,第九周期結(jié)束,一個(gè)BYTE傳送完成.
Ø 讀回應(yīng):主設(shè)備讀從設(shè)備八位數(shù)據(jù)后,也應(yīng)該在第九周期進(jìn)行回應(yīng),分如下兩種情況: 連續(xù)讀n個(gè)字節(jié)時(shí),前n-1個(gè)字節(jié)以拉低作回應(yīng),第n個(gè)字節(jié)則為拉高SDA回應(yīng),因此如若是每次只讀一個(gè)字節(jié),則回應(yīng)為拉高.
4. 操作SDA/SCL PIN時(shí),注意在讀SDA時(shí),將其設(shè)置成輸入狀態(tài).
文件: linux-2.4.21\drivers\i2ci2c-ep93xx.c
描述: SDA/SCL PIN腳定義, 為EP93xx的GPIO口G口的第0, 1位.
#define I2C_SDA_PORT GPIO_PGDR
#define I2C_SDA_DIR GPIO_PGDDR
#define I2C_SDA_MASK 0x2 //EEDAT...
#define I2C_SCL_PORT GPIO_PGDR
#define I2C_SCL_DIR GPIO_PGDDR
#define I2C_SCL_MASK 0x1 //EECLK...
文件: linux-2.4.21\drivers\i2c\ i2c-ep93xx.c
描述: SDA/SCL輸入輸出.
static void bit_ep93xx_setscl(void* data, int state)
{
unsignedlong flags;
save_flags(flags);
outl(inl(I2C_SCL_DIR)| I2C_SCL_MASK, I2C_SCL_DIR); //tristate pin
if (state){
cli();
outl(inl(I2C_SCL_PORT)| I2C_SCL_MASK, I2C_SCL_PORT); // drive pin
}
else{
cli();
outl(inl(I2C_SCL_PORT)& ~I2C_SCL_MASK, I2C_SCL_PORT); // drive pin
}
restore_flags(flags);
}
//===========================================================================
/// write SCL pin
//===========================================================================
static void bit_ep93xx_setsda(void* data, int state)
{
unsignedlong flags;
save_flags(flags);
outl(inl(I2C_SDA_DIR)| I2C_SDA_MASK, I2C_SDA_DIR); //output...
if (state){
cli();
outl(inl(I2C_SDA_PORT)| I2C_SDA_MASK, I2C_SDA_PORT); // drive pin
}
else{
cli();
outl(inl(I2C_SDA_PORT)& ~I2C_SDA_MASK, I2C_SDA_PORT); // drive pin
}
restore_flags(flags);
}
具體有關(guān)I2C總線軟件模擬通信部分,參考源碼.
六. Linux下的I2C驅(qū)動(dòng)層次結(jié)構(gòu).
Linux最大的特點(diǎn),就是將一系列的驅(qū)動(dòng)中共有的東西抽象出來,大大提高代碼的共享與利用率,這樣使具體硬件設(shè)備的驅(qū)動(dòng)作者無須關(guān)注驅(qū)動(dòng)中共性的部分,I2C驅(qū)動(dòng)同樣如此,這里我不打算詳細(xì)的分析I2C的層次結(jié)構(gòu),只是簡(jiǎn)單的陪析一下其大略的層次, 并指出幾個(gè)不易理解的地方。
1. 適配器層(adapters)
描述: 這一層簡(jiǎn)單的理解,可以理解成是I2C總線的抽象,它提供訪問I2C總線的方法規(guī)則,并包含在物理總線上適用此規(guī)則進(jìn)行I2C通信的I2C設(shè)備, 而且I2C設(shè)備并不須是在同一條總線上,只須滿足相同的訪問I2C總線方法規(guī)則,且設(shè)備地址不重重疊.
2. I2C設(shè)備驅(qū)動(dòng)層(i2c client driver)
描述:具體到每個(gè)掛在I2C上的設(shè)備, 針對(duì)特定的設(shè)備可以有不同的驅(qū)動(dòng), 諸如設(shè)備標(biāo)志ID, 設(shè)備名稱, 設(shè)備屬性志Flag, 還有就是設(shè)備連接及拔除的相應(yīng)處理.
3. I2C數(shù)據(jù)傳送規(guī)則層(algorithms)
描述: 針對(duì)具體的I2C總線, 可以有不同的數(shù)據(jù)傳輸規(guī)則, 分為三種:
ü 大體來說如果本身就有I2C控制器的, I2C使用起來就比較簡(jiǎn)單, 傳輸入數(shù)據(jù)時(shí)就是讀寫一些寄存器即可.
ü 通地軟件模擬方式進(jìn)行數(shù)據(jù)傳輸, I2C中這個(gè)規(guī)則被稱為algorithmsfor bit-shift, 這種編程起來稍微麻煩一些, 要用軟件模擬I2C的通信協(xié)議規(guī)則.
ü 通過每三方總線間接的連接I2C總線的, 如ISA總線, 此時(shí)I2C是間接連接在系統(tǒng)上, 這種方式通常與具體的所連接上的總線相關(guān).
另外, 還有一種規(guī)則是針對(duì)SMBus總線, 這個(gè)總線是Intel推出的兼容I2C協(xié)議的, 可能一次傳送一個(gè)字,兩個(gè)字節(jié),多個(gè)字節(jié)等等, 這里沒有用到,并不詳述, 由此我們可以理會(huì)I2C設(shè)計(jì)者設(shè)計(jì)的結(jié)構(gòu)的靈活程度.
以上簡(jiǎn)述了I2C驅(qū)動(dòng)層次結(jié)構(gòu), 現(xiàn)在具體的就PCF8563 RTC來描述每一層具體由哪些文件組成, 結(jié)合實(shí)際的驅(qū)動(dòng)文件說明:
1. 適配器層----------------- i2c-core.c, i2c-dev.c
2. I2C設(shè)備驅(qū)動(dòng)層---------pcf8563-rtc.c
3. I2C數(shù)據(jù)傳送規(guī)則層--- i2c-algo-bit.c,i2c-ep93xx
大體對(duì)照I2C驅(qū)動(dòng)的層次結(jié)構(gòu), 結(jié)合以上文件來看, 可以理解I2C的驅(qū)動(dòng).
I2C驅(qū)動(dòng)代碼中不易理解的幾點(diǎn):
結(jié)合在代碼閱讀時(shí)我所經(jīng)歷的過程, 說明以下幾點(diǎn)不易明白的地方:
1. i2c設(shè)備的識(shí)別
描述:每個(gè)I2C設(shè)備有自己特定的設(shè)備地址,通常為讀地址及寫地址; 2.4版當(dāng)中是采取遍歷的方式來查找I2C設(shè)備應(yīng)該, 每個(gè)i2c設(shè)備都會(huì)標(biāo)明自己所處的地址,描述i2c設(shè)備所處地址范圍的結(jié)構(gòu)比較復(fù)雜, 它包括描述i2c設(shè)備所處地址范圍, 要忽略的地址范圍, 應(yīng)當(dāng)強(qiáng)制檢測(cè)的范圍等, 個(gè)人感覺些結(jié)構(gòu)設(shè)計(jì)過于重復(fù), 在2.6版中已經(jīng)簡(jiǎn)化.
具體的I2C設(shè)備檢測(cè)時(shí), 即根據(jù)這些地址, 從0~0x7f開始檢測(cè), 檢測(cè)時(shí)會(huì)跳出過無須檢測(cè)的范圍. 比如一i2c設(shè)備寫地址為0xa0, 則在此描述時(shí)給出的地址值是0x50, 因此在真實(shí)寫此設(shè)備時(shí), 必須將地址左移兩位得真實(shí)地址.
2.4版i2c地址描述結(jié)構(gòu):
structi2c_client_address_data {
unsigned short *normal_i2c;
unsigned short *normal_i2c_range;
unsigned short *probe;
unsigned short *probe_range;
unsigned short *ignore;
unsigned short *ignore_range;
unsigned short *force;
};
2.6版i2c地址描述結(jié)構(gòu):
structi2c_client_address_data {
unsigned short *normal_i2c;
unsigned short *probe;
unsigned short *ignore;
unsigned short **forces;
};
2. i2c設(shè)備設(shè)備的讀與寫
描述:設(shè)備地址一般為7位, 此地址是字節(jié)的高七位, 最低的一位用于描述是讀設(shè)備還是寫設(shè)備, 因此可知I2C設(shè)備讀寫地址必然相連, 因此讀寫地址轉(zhuǎn)換通常為最低位或上1, 或者清除1, 另外還記得這里描述的地址必須左移兩位, 因?yàn)榻o出的設(shè)備地址是以低七位形式給出.
寫地址一般是小于讀地址, 所以通常讀地址都是寫地址或上1即得, 因此在設(shè)備驅(qū)動(dòng)結(jié)構(gòu)i2c_client中, 僅用一addr成員來描述設(shè)備地址.
具體來說, 看i2c-algo-bit.c文件下的bit_doAddress()函數(shù), 如果是七位地址的I2C總線, 其處理如下, 將設(shè)備地址左移兩位得寫地址,然后或上1, 即得讀地址.
addr= ( msg->addr << 1 );
if(flags & I2C_M_RD )
addr|= 1;
3. i2c adapters 及algorithms的管理.
描述: 每一個(gè)adapters 都會(huì)有一個(gè)唯一的標(biāo)志, 如我們的EP93xx則定義為I2C_HW_B_EP93XX, 每一種algorithms都也有一個(gè)ID標(biāo)志, bit-shift algorithms的標(biāo)志即為: I2C_ALGO_BIT., 關(guān)于這些標(biāo)志的定義, 可以查看i2c-id.h文件.
具體的, 添加一個(gè)bit-shift algorithms的adapters , 可以調(diào)用i2c_bit_add_bus()函數(shù), 這個(gè)調(diào)用由用戶發(fā)出, 一般在模塊加載的init函數(shù)中調(diào)用, 每當(dāng)加入一個(gè)adapters, 都會(huì)檢測(cè)已經(jīng)注冊(cè)的i2c driver設(shè)備是否適用此adapters, 如果適用則調(diào)用該i2c設(shè)備driver下的attach_adapter, 通知設(shè)備發(fā)現(xiàn)其相應(yīng)適配器, 只有找到相應(yīng)適配器, 該設(shè)備才能使用i2c總線.
聯(lián)系客服