(原創) 鞏固理解I2C協議(MCU,經驗)

    題外話:這幾每天氣忽然轉冷了。今天已經是11月23日了,查查黃曆,昨天(11月22日)恰好是小雪,一晚上溫度驟降,果真老祖先的經驗有靈驗!冬天來了,仍是多加加衣服,注意保暖!算法

1.Abstract小程序

    前些天借用他人的一塊MCS-51開發板來作實驗,不想這塊板子與我剛開始接觸MCS-51的板子同樣,實在是太親切了!如今回過來看這塊板子,功能算不上是太強大,麻雀雖小五臟俱全,該有的功能都有。因而又忍不住搗騰這塊板子,倒不是寫小程序一塊,看着電路圖,處處連線測試一下功能,從中體會下最初的學習興奮感受。安全

    最初板子裏邊最難學會的有兩處,一處是由I2C上掛上的一些器件,另一處是基於DS18B20的一線傳輸協議,當初花了好大氣力去學習,仍舊一頭霧水的情景仍記憶猶新。如今回過來看,以爲當時最大的問題是不注重對時序的分析,不能徹底理解總線傳輸協議,畢竟理解是隨着時間的增長而逐步深入的。看到這兩大部分,雖然是幾個小小的芯片,但心底有很多的辛酸感。如今的理解也不能算徹底,我想盡量的用如今的所學將這兩大部分分別寫下來,權當是一個鞏固學習的過程。學習

2.Content測試

  2.1 協議分析優化

    先想一想兩個陌生人是怎麼進行溝通的,爲了顯得更有主次關係,選取老闆和新員工進行溝通的場景,老闆通常佔有主動權,並且手中有新員工的基本信息,好比姓名,年齡,性別等。溝通開始:網站

    老闆:「XXX,歡迎你加入咱們公司,爲公司注入新的血液!」伸出握手姿式              —— 主握手編碼

    新員工:「承蒙厚愛,有幸加入咱們公司,我以爲是一種光榮!」握手                      —— 握手成功spa

    老闆:「想必已經讀過員工手冊了吧,說說你對前兩條的理解。」                            —— 執行溝通.net

    新員工:「第一條……,第二條……」                                                                    —— 從應答

    ……

    老闆:「回答的很好,確實是咱們迫切想招募的人,之後看你的精彩表現了!」        —— 主要求溝通結束

    新員工:「必定不負厚望,必努力工做!」                                                            —— 從作好結束準備

    老闆離開                                                                                                             —— 溝通結束

    爲了跟後邊的描述更貼切,在實景對話的後邊作了必定的註釋,將溝通的過程抽象出來。從上面的對話能夠看出,溝通分爲四大步驟,握手、數據交換、準備結束、正式結束。I2C的通訊也氛圍這樣幾大步驟,值得注意的是,在通訊的時候,總線上必需要有一個爲主器件,其餘的爲從器件。有些器件它既能夠是主器件,又能夠是從器件,可是它們在總線上的特定時候只有一種模式,要麼是主器件模式,要麼是從器件模式,不然就混亂了。

    把那些在特定時候器件做爲主器件,即在總線上表現爲主控的器件叫作主發送器/接收器(MASTER TRANSMITTER/RECEIVER),有些主器件只執行發送,不執行接收的,命名爲主發送器(MASTER TRANSMITTER);那些在同時候做爲從器件,即在總線上表現爲被控的器件叫作從發送器/接收器(SLAVE RECEIVE/RTRANSMITTER),有些從器件只執行接收,不執行發送的,命名爲從接受器(SLAVE RECEIVER)。它們在同一個總線上的鏈接能夠如FIG2.1所示。

image FIG2.1 I2C總線的器件鏈接

    值得強調的一下是 同一時刻,總線上只有一個器件配置爲主器件,而其餘的只能做爲從器件!

    I2C協議是典型的二線通訊,確切的講,應該是三根(SDA串行數據線,SCL串行時鐘線,GND共地線),如上圖所示。

    那麼,一條I2C總線最多能掛多少個器件呢?通常來講,串行數據都是以一個字節一個字節的方式來衡量的,前幾位用來表示地址(上述對話中的員工名字),地址的最後一位爲讀寫操做位(READ/WRITTE位,簡寫爲R/W,邏輯1表示讀,邏輯0表示寫),以最開始的一個字節做爲地址來算的話,那麼除去字節的最後讀寫標誌位,就剩 8 – 1 = 7bit了,因此理論上以1個字節爲地址來算的話,就能夠分配2的7次方128個地址,就能夠掛上128個器件(極端狀況,假設這128個器件都是從機的話,就還須要掛一個主器件,合計就是128 + 1 = 129個器件了)。要是想掛更多的器件,那麼就須得將地址位擴展,好比將前兩個字節做爲地址,去掉最後的一位讀寫標誌位,就剩下 8 + 8 – 1 = 15 bit了,因此以2個字節爲地址來算的話,就能夠分配2的15次方32768個器件(固然,極端狀況下能夠多掛一個主器件,合計就是32768 + 1 = 32769個器件了)。要是還想掛更多的器件,方法就如上述了,將前幾個字節做爲地址,最後一位做爲讀寫標誌位,具體的算就不展開了。

    以上是理論的算法,在實際的器件中,都是以第一個字節做爲地址的,並且大部分的器件的地址高四位已經被根據不一樣功能的芯片分配了不一樣的編碼(例如,AD/DA轉換芯片PCF8591的前四位爲1001,E2PROM芯片AT24C02的前四位爲1010,具體的芯片就得查查手冊了,這裏只說明原理),那麼同一種功能芯片(地址前4位都相同)最多隻有 8 – 4 – 1 = 3位用來分配地址了,也就是最多能夠掛2的3次方8個同種功能芯片。用一個問題來深化理解一下。

    一條I2C總線上最多能夠分別掛多少個PCF8591芯片和多少個AT24C02芯片呢?它們能同時掛在總線上嗎?若能,請將它們的地址所有列出來。

    算算,1)查手冊得知,PCF8591的前四位固定編碼爲1001,除去最後一位讀寫標誌位,則剩下8 – 4 – 1 = 3位地址編碼,由數學邏輯可知,3位二進制碼能夠分配2的3次方8個地址,故一條I2C總線上最多能夠掛8個PCF8591;同理,AT24C02的前四位固定編碼爲1010,其餘的跟PCF8591同樣,故一條I2C總線上最多能夠掛8個AT24C02。2)它們是能夠同時掛在一條總線上的,由於它們對應的地址都不相同,主器件能夠所有訪問到它們。地址以下表

表2.1 器件地址表

器件名稱 器件地址
PCF8591 1001 000X
PCF8591 1001 001X
PCF8591 1001 010X
PCF8591 1001 011X
PCF8591 1001 100X
PCF8591 1001 101X
PCF8591 1001 110X
PCF8591 1001 111X
AT24C02 1010 000X
AT24C02 1010 001X
AT24C02 1010 010X
AT24C02 1010 011X
AT24C02 1010 100X
AT24C02 1010 101X
AT24C02 1010 110X
AT24C02 1010 111X

    關於總線掛器件的問題,應該是明晰許多了。下面開始看看它的四大通訊步驟。

  2.1.1 握手與結束

    握手與結束它們都是屬於控制總線一起的,抽象出來講,它是作通訊的控制的,跟數據是如何交換的區分開來。

image FIG2.2 I2C總線開始和結束

    I2C協議有規定,在SCL和SDA均爲高電平的前提下,檢測到SDA有降低沿信號,則創建I2C的通訊開始;一樣的,在SCL爲高電平,SDA爲低電平的前提下,檢測到SDA有上升沿信號,則I2C通訊正式結束。

    對上面的一段話還真須要有深入的理解,甚至有必要將它背下來,熟記於心。首先看看總線閒,也就是總線上沒有通訊,這兩根信號線的電平狀態;從圖中能夠看到,通訊的開始以前,SCL和SDA均爲高電平;再看看通訊正式結束後,SCL和SDA信號均爲高電平;也就是說,總線閒的時候,兩根信號線都是高電平的。再看看通訊的創建和正式結束的時候,這兩根信號線的電平變化特色。由圖中虛線框中引出的,無論是通訊創建和通訊結束階段,SCL都是高電平,SDA的變化控制着通訊的創建與結束;這一點尤其重要,或許換一種說法更爲適合,在SCL爲高電平的狀況下,SDA信號的轉變就對通訊起着強制性做用,要麼通訊創建,要麼通訊正式結束,有且僅有這兩種狀況!也就是說,在數據交換的過程當中,要對SCL這根信號線尤其注意,在數據變化的時候,必定要保證SCL是爲低電平!讓數據的變化在SCL的「安全」狀態下進行,因此須要牢記一點,數據變化,時鐘線低電平先行,以下圖所示

image FIG2.3 數據時序規則

   2.1.2 數據交換

    爲了更好地理解I2C數據交換的過程,真是花了很多的功夫,或許真正難懂的地方就在這裏,須要準確無誤的理解。

    I2C總線上時常是掛着許多器件的,可是某個通訊時刻下,只存在一個主器件,一個從器件的一對一通訊機制;這也是要分配地址編碼的真正緣由了,主器件將數據傳送到總線上,雖然全部的器件都能接收到數據,可是隻有對應地址的器件能作出應答反應。理解這一點就好辦了;從主器件這一端看過去,須要和某個從機打交道,那麼它應該具有的信息有:1) 從器件的地址 2)從器件內部的控制信息;對應於人的邏輯思惟應該是 對方的姓名和要求他作特定的事兒。每次通訊開始前,主器件都須要將這兩個信息交代清楚。

    仍是站在主器件的角度來看;綜合一下,主器件主要作哪些事兒呢?是的,無非兩件,1、寫數據,2、讀數據,除此兩者以外,別無其餘。這對應人的思惟應該就是說和聽了。引出了主器件寫數據和讀數據的概念,再就看它們是如何工做的了。

    主器件向從器件寫數據。如上所述,主器件向要向從器件寫數據,首先得知道從器件的兩個基本信息,從器件地址和從器件內部控制信息。寫這些信息有特定的順序,第一個字節是從器件的地址,第二個字節是從器件內部控制信息,第三個字節是所要寫的第一個數據,第四個字節是所要寫的第二個數據……完整寫的通訊過程就應該是以下圖所示了。

image FIG 2.4 主器件寫操做流程

    紫色部分表示通訊控制,橙色表示寫必要的通訊信息,綠色部分表示要寫的數據。

    實際的I2C通訊大概流程就是如此了,可是它作了必定的優化,即在每次寫數據的時候,每寫一個字節數據(不管是必要的從器件地址數據、從器件控制數據,仍是須要寫到從機裏的數據),從器件都有一個應答(從器件的應答實現是將SDA線拉成低電平,因此在寫完第8位數據之後,切記要把SCL的電平拉成低電平,等一小段時間之後,在將數據線拉高去讀SDA的數據,至於緣由,2.1.1節描述得很清晰了),用下面的情景模式解釋一下或許更容易理解。

    對話已經開始                                                                                              —— 通訊開始

    老闆:「XXX,你來一下!」                                                                         —— 寫從器件地址

    員工:「是!」                                                                                           —— 從器件應答

    老闆:「你管業務這一塊的吧?」                                                                 —— 寫從器件控制信息

    員工:「是!」                                                                                           —— 從器件應答

    老闆:「分配兩個任務,其一,今天下午2:30大家組開會!」                          —— 寫數據1

    員工:「是!」                                                                                           —— 從器件應答

    老闆:「其二,下班後你們一塊兒出去吃飯!」                                                 —— 寫數據2

    員工:「是!」                                                                                           —— 從器件應答

    老闆:「好了,就這麼多了,去忙吧!」                                                       —— 寫數據3

    員工:「是!」                                                                                           —— 從器件應答

    對話結束                                                                                                     —— 通訊正式結束

    也就是說,老闆每說一段話,員工都必須作出是的回答。轉換成I2C協議裏邊,就是主器件每發送一個字節數據,從機都要作一個應答(用ACK表示,簡寫爲A)。

image FIG2.5 I2C通訊中主器件完整的通訊過程

    這個是以PCF8591爲例子的,其餘的器件相似。從左往右看,最開始的是通訊創建S;而後再寫從器件地址(注意,器件地址的最後位爲0,表示的是寫操做);緊接着器件回覆了一個應答信號A;而後主器件寫從器件的控制信息CONTROL BYTE;從器件回覆一個應答信號A;以後主器件開始寫數據DATA BYTE;寫完一個數據之後,從器件回覆一個應答信號A;固然,能夠寫多個數據,每寫完一個數據,從器件都回復一個應答信號A;寫完全部數據以後,能夠選擇中止通訊操做P,也能夠創建新的開始通訊S。主器件的寫就講述完了,再來看主器件的讀。

    主器件向從器件讀數據。主器件向從器件讀數據的過程和上述主器件向從器件寫數據的過程相似,也得首先知道從器件的地址信息,對於控制信息,有些器件須要,有些器件則不須要,這得查看手冊信息了。具有這些信息之後,就能夠開始讀器件的數據了。如今以一個最基本讀流程開始,後邊再介紹一個稍微複雜的讀流程。

    以PCF8591爲例,對照它的數據手冊,可知它的功能是一個8位4路A/D轉換和1路D/A轉換的集成芯片。在作D/A轉換時,主要用到的是對它進行寫的操做;而用到讀取它的數據,則是A/D轉換的操做;由於每次A/D採樣完成之後,就會把數據放到指定的地方,並且只有一個字節數據,因此對它的讀操做就不須要控制信息了,主器件只須要有它的器件地址就能夠完成讀的操做了。這樣就簡化了流程,主器件只須要發送從器件的地址信息加上讀操做位,就能夠對信息的讀取了。完整的流程以下。

image FIG 2.6 主器件的讀操做流程

    主器件首先要創建通訊,而後向總線發送 器件地址+ 讀標誌位,從器件就能夠將數據一位一位將數據發到總線上,要注意的是,雖然主器件不能控制SDA信號了,可是它能夠控制SCL信號,主機能夠經過改變SCL的電平,從而來接收來自於SDA的信息。和寫操做同樣,在每發送1個字節數據之後,須要有一個應答信號;這下應答信號的產生就來自於主器件了。能夠這樣來看,發送方式從器件,而接收方是主器件,發送方要確認接收方已經接收到數據了,這種發送方和接收方的理解也適用於上述的讀操做。

    比較特殊的是,主器件的應答方式有兩種。一種是將SDA線成低電平,而後輸出一個脈衝,從器件根據接收到這個低電平信號,能夠判斷主器件正常地接收到了數據,並且準備好了接收下一個數據,這與上述的寫操做從機應答是同樣的;還有一種就是主器件將SDA釋放爲高電平,而後輸出一個脈衝,從器件根據接收到的這個高電平信號,能夠判斷主器件正常地接收到了數據,可是不許備接收下下一個數據,意味着數據通訊即將結束。這種應答方式能夠用下面的情景模式輔助理解一下。

    對話開始                                                                                    —— 創建通訊

    老闆:「XXX,你過來一下,把今天的完成的進度彙報一下!」       —— 發送從機地址並加讀信號標誌位

    員工:「今天完成的任務有四項,第一項是……」                         —— 從器件發送數據1

    老闆:「嗯,繼續」                                                                    —— 主器件肯定應答

    員工:「第二項是……」                                                             —— 從器件發送數據2

    老闆:「好,就這樣吧,還彙報一個任務你就去忙吧!」               —— 主器件取消應答

    員工:「第三項是……」                                                             —— 從器件發送數據3

    對話結束                                                                                    —— 通訊正式結束

    也就是說,員工彙報完每項任務之後,老闆都須要作一個應答,應答的方式有兩種,一種是作好準備聽下下一個任務,另一種是結束聽下下一個任務,從而結束對話。轉換到I2C協議中來,就是從器件自接收地址數據要求作數據輸出時,從器件開始向總線逐個的發送數據;主器件也須要對從器件發送的數據進行應答;應答的方式有兩種,一種是發送應答信號(ACK),表示主器件已經準備好接收下下一個數據,另一種就是非應答信號(NO ACK),表示主器件不打算接收下下一個數據,要求通訊結束。PCF8591完整的讀操做過程以下所示。

image FIG2.7 針對PCF8591芯片主器件完整的讀操做流程

    仍是從左往右看,最開始是創建通訊開始S;而後主器件向總線上發送從器件地址,並將讀標誌位置1(地址的最後一位置1,表示讀操做);隨後,從器件將第一個數據發送到I2C總線上;若是主器件準備再讀取下下一個數據,則置應答位爲0(ACK),若是主器件不想再讀下下一個數據,就置應答位爲1(NO ACK),表示通訊即將結束;最後就是通訊結束S,表示通訊正式結束。

    PCF8591的讀操做流程仍是很是簡單的,由於所要讀的數據很少,數據位置已經固定好了,因此沒有控制信息。再來看一下一個具備控制信息的器件該怎麼讀,以E2PROM存儲器AT24C02爲例。

    針對存儲器,首先要知道的就是它的器件地址,其次就是要讀的地址信息(也就是控制信息)。因此,在讀數據以前就要先將地址信息寫過去,而後才能準確地讀數據。例子就不舉了,跟PCF8591大同小異,多了一個在讀以前要寫控制信息這部分。直接看看完整的讀操做流程。

image FIG 2.8 針對AT24C02芯片主器件完整的讀操做流程

    仍是從左往右看,要想讀取特定地址的數據,首先要將地址寫入到指定器件。按照操做流程,最開始是創建通訊S,而後寫從器件地址,並將讀寫操做位置0(地址的最後1位爲0,表示寫操做);緊接着是從器件回覆應答信號(ACK);而後寫入要讀出的數據信息位置(控制信息);從器件回覆應答信號(ACK);而後再開始進行讀操做,讀操做以前,也先將從器件的地址發送到I2C總線上,並將讀寫標誌位置1(地址最後一位置1,表示讀操做);而後從器件回覆一個應答信號(ACK);緊接着從器件將指定位置的數據發送到I2C總線上,主器件接收到數據之後,發送一個非應答信號(NO ACK),表示再也不準備讀取下下一個數據,準備通訊結束;最後就是通訊結束S,表示通訊正式結束。

    上述描述了主從器件之間如何握手與通訊結束,如何向從機寫數據,如何從從機讀數據等基本協議分析。下面就是進行用程序來實現這個通訊協議。

2.2 協議的程序實現

    協議的程序實現部分,我本身就再也不寫代碼了,上述的協議圖已經分析的很透徹了,這裏我參考別人寫好的程序,其實實現的方式也是大同小異,由於通訊的格式已經固定了,具體的代碼都得按照這個格式來書寫。

    個人這份代碼摘自於doflye網站:www.doflye.net 它是以PCF8591爲例,其餘的類推就能夠了。

  2.2.1 基本配置和宏定義

#include "i2c.h"
#include "delay.h"

#define  _Nop()  _nop_()  //定義空指令
                         
bit ack;                  //應答標誌位

sbit SDA=P2^1;
sbit SCL=P2^0;

  2.2.1 I2C通訊創建

void Start_I2c()
{
  SDA=1;   //發送起始條件的數據信號
  _Nop();
  SCL=1;
  _Nop();    //起始條件創建時間大於4.7us,延時
  _Nop();
  _Nop();
  _Nop();
  _Nop();    
  SDA=0;     //發送起始信號
  _Nop();    //起始條件鎖定時間大於4μ
  _Nop();
  _Nop();
  _Nop();
  _Nop();       
  SCL=0;    //鉗住I2C總線,準備發送或接收數據
  _Nop();
  _Nop();
}

    這裏有幾個位置須要說明一下,就是時序上的衝突問題。

image FIG2.9  PCF8591的操做時序

imageFIG2.10 PCF8591 交流特性表

    FIG2.9 和 FIG2.10是須要合起來一塊兒看,並且通常是要找出幾種極限的參數,好比時序創建時間、保持時間、有效數據時間等。對照圖和表,查出時序創建時間Tsu至少爲4.7us,保持時間Thd至少爲4.0us,數據有效時間創建不低於3.4us。

    若MCS-51選取12M的晶振,每個指令週期的時間就是 12 / ( 6 * 12) = 1us,故達到了器件工做頻率的上限值,因此須要採起必定的空機器週期用來解決時序衝突。下面也有這樣的作法,就再也不贅述了。

  2.2.2 I2C通訊結束

void Stop_I2c()
{
  SDA=0;    //發送結束條件的數據信號
  _Nop();   //發送結束條件的時鐘信號
  SCL=1;    //結束條件創建時間大於4μ
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SDA=1;    //發送I2C總線結束信號
  _Nop();
  _Nop();
  _Nop();
  _Nop();
}

  2.2.3 主器件向從器件寫一個字節數據(帶應答信號檢驗)

void  SendByte(unsigned char c)
{
 unsigned char BitCnt;
 
 for(BitCnt=0;BitCnt<8;BitCnt++)  //要傳送的數據長度爲8位
    {
     if((c<<BitCnt)&0x80)SDA=1;   //判斷髮送位
       else  SDA=0;                
     _Nop();
     SCL=1;               //置時鐘線爲高,通知被控器開始接收數據位
      _Nop(); 
      _Nop();             //保證時鐘高電平週期大於4μ
      _Nop();
      _Nop();
      _Nop();         
     SCL=0; 
    }
    
    _Nop();
    _Nop();
    SDA=1;               //8位發送完後釋放數據線,準備接收應答位
    _Nop();
    _Nop();   
    SCL=1;
    _Nop();
    _Nop();
    _Nop();
    if(SDA==1)ack=0;     
       else ack=1;        //判斷是否接收到應答信號
    SCL=0;
    _Nop();
    _Nop();
}

  2.2.4 主器件從從器件接收一個數據

unsigned char  RcvByte()
{
  unsigned char retc;
  unsigned char BitCnt;
  
  retc=0; 
  SDA=1;             //置數據線爲輸入方式
  for(BitCnt=0;BitCnt<8;BitCnt++)
      {
        _Nop();           
        SCL=0;       //置時鐘線爲低,準備接收數據位
        _Nop();
        _Nop();      //時鐘低電平週期大於4.7us
        _Nop();
        _Nop();
        _Nop();
        SCL=1;       //置時鐘線爲高使數據線上數據有效
        _Nop();
        _Nop();
        retc=retc<<1;
        if(SDA==1)retc=retc+1; //讀數據位,接收的數據位放入retc中
        _Nop();
        _Nop(); 
      }
  SCL=0;    
  _Nop();
  _Nop();
  return(retc);
}

  2.2.5 主器件回覆應答信號0(ACK)

void Ack_I2c(void)
{
  
  SDA=0;     
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();              //時鐘低電平週期大於4μ
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;               //清時鐘線,鉗住I2C總線以便繼續接收
  _Nop();
  _Nop();    
}

  2.2.6 主器件回覆應答信號1(NO ACK)

void NoAck_I2c(void)
{
  
  SDA=1;
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();              //時鐘低電平週期大於4μ
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;                //清時鐘線,鉗住I2C總線以便繼續接收
  _Nop();
  _Nop();    
}

  2.2.7 主器件向從器件寫入數據

bit WriteDAC(unsigned char dat)
{
   Start_I2c();               //啓動總線
   SendByte(AddWr);             //發送器件地址
     if(ack==0)return(0);
   SendByte(0x40);            //發送控制信息
     if(ack==0)return(0);
   SendByte(dat);             //發送數據
     if(ack==0)return(0);
   Stop_I2c();  
}

  2.2.8 主器件從從器件讀入數據

unsigned char ReadADC(unsigned char Chl)
 {
   unsigned char Val;
   Start_I2c();               //啓動總線
   SendByte(AddWr);             //發送器件地址
     if(ack==0)return(0);
   SendByte(0x00|Chl);            //發送控制信息  AD採樣通道
     if(ack==0)return(0);
   Start_I2c();
   SendByte(AddWr+1);    //  將最後一位讀寫標誌位設置爲1,表示讀
      if(ack==0)return(0);
   Val=RcvByte();
   NoAck_I2c();                 //發送非應位
   Stop_I2c();                  //結束總線
  return(Val);
 }

  2.3  實際程序驗證

    最後加入一些液晶顯示模塊和AD轉換數據處理,將信息能夠顯示在液晶屏幕上。由於這套板子的程序已經徹底作好了,在出廠以前就已經測試好了;爲了保險起見,我也將程序燒錄其中作了一下測試,其實是能夠通得過的。這裏就再也不插圖看了,直接將測量結果以表格的方式給出來,避免打廣告的嫌疑。

表2.2 數據測量表

測量次數 萬用表測試值(V) AD轉換後對應的值(V)
1 0.00 0.00
2 1.56 1.60
3 2.45 2.52
4 4.87 4.98

    萬用表的型號是LINI-T UT51。從上述的測試值對比,轉換的值仍是相差挺大的,雖然A/D轉換器存在量化偏差,可是不至於這麼大。分析了下緣由,實際上是受參考電壓的影響,A/D轉換計算是採用5.00V的參考電壓,而實際的參考電壓爲4.87V,根據轉換的公式,能夠知道計算出來的電壓與實際電壓有一個比值係數 k = 5.00/4.87,有了這個關係,把AD轉換後的值除以這個係數,就能夠獲得實際的電壓了,以第二次測試結果爲例說明一下。

    1.60 / k = 1.60 * 4.87 / 5.00 = 1.5584,取小數後最後兩位,第三位小數位進行四捨五入處理,那麼實際的轉換值爲1.56,這與萬用表的測試值是相吻合的。

    關於器件的量化偏差,與本節的內容就不大相關了,這裏就不分析這部分了。上述實驗證實,經過MCU與PCF8591的I2C通訊是成功的。

3.Conclusion

    整體來講,I2C的協議仍是不復雜的,不過學好它確實有點不容易,須要從多個角度去思考和體會。如今規範的協議不算太多,可以很好的掌握I2C協議就至關於上了一個新的臺階。

    就我的來講,對I2C的協議理解仍是不夠深入的,之前學了一門課程,計算機接口與技術,這門課是專門講接口的,I2C就是一個很經常使用的串行接口。不過這本書的主要目的是講硬件方面,即這些接口是怎樣用物理的方法實現的。對於使用者,確確實實只要瞭解這個接口怎麼用就能夠了,但想從電路設計的角度去深入理解它,我想要學的還有不少。

4.Reference

1). PCF8591 datasheet

2). AT24C02 datasheet

相關文章
相關標籤/搜索