IIC總線是一個雙向的兩線連續(xù)總線,它為集成電路之間提供通信線路。其意思是完成集成電路或功能單元之間信息交換的協(xié)議。
01IIC協(xié)議時序介紹
IIC協(xié)議的時序簡單來說就是兩條線之間的關(guān)系,分別為時鐘線SCL和數(shù)據(jù)線SDA,先看IIC協(xié)議的時序圖。

上圖為IIC的時序圖,該時序圖的狀態(tài)分為空閑態(tài)、起始位、停止位還有數(shù)據(jù)的讀/寫狀態(tài)。
1、空閑狀態(tài):數(shù)據(jù)線SDA為高電平,時鐘線SCL為高電平;
2、起始位:時鐘線SCL為高電平,數(shù)據(jù)線SDA出現(xiàn)一個下降沿,由此產(chǎn)生一個起始位;
3、停止位:時鐘線SCL為高電平,數(shù)據(jù)線SDA出現(xiàn)一個上升沿,由此產(chǎn)生一個停止位;
4、數(shù)據(jù)讀/寫狀態(tài):數(shù)據(jù)的讀寫狀態(tài)主要包括串行數(shù)據(jù)的輸入和輸出以及應(yīng)答信號,讀寫數(shù)據(jù)的具體的時序圖如下所示。

當(dāng)兩個設(shè)置之間采用IIC通信時(其中一個為主機(jī),另一個為從機(jī)),當(dāng)主機(jī)向從機(jī)寫入數(shù)據(jù)時,SDA上的每一位數(shù)據(jù)均在SCL的高電平期間被寫入從機(jī)中。因此在主機(jī)需要在數(shù)據(jù)線SCL為低電平期間改變SDA上要寫入的數(shù)據(jù)。當(dāng)主機(jī)需要讀取從機(jī)上的數(shù)據(jù)時,從機(jī)則需要在SCL為低電平期間將數(shù)據(jù)輸出到SDA數(shù)據(jù)線上,在SCL為高電平期間保持?jǐn)?shù)據(jù)的穩(wěn)定,而主機(jī)則需要在SCL為高電平時將SDA線上的數(shù)據(jù)讀取并存儲起來。
每當(dāng)一個字節(jié)的數(shù)據(jù)傳輸完成時,數(shù)據(jù)接收方都會向數(shù)據(jù)發(fā)送方發(fā)送一位的應(yīng)答信號,即響應(yīng)位。在響應(yīng)應(yīng)答位時,數(shù)據(jù)發(fā)送方將SDA線設(shè)置為三態(tài)門輸入,在IIC總線上,數(shù)據(jù)線SDA都有一個上拉電阻,即一般情況下SDA都默認(rèn)為高電平,若數(shù)據(jù)接收方正確接收到數(shù)據(jù)后,則數(shù)據(jù)接收方將會將SDA線的電平拉低,表示正確應(yīng)答。
02IIC程序設(shè)計
首先來看需要定義的端口,分別為時鐘源clk,復(fù)位端rst,cmd為指定此次傳輸是否需要加起始位或停止位,go為啟動IIC的信號,rx_data為發(fā)送的數(shù)據(jù),tx_data為接收的數(shù)據(jù),trans_done為IIC數(shù)據(jù)傳輸完成標(biāo)志,ack_o為應(yīng)答信號,i2c_scl為時鐘線SCL,i2c_sda為數(shù)據(jù)線SDA。定義如下圖所示。

接下來便是分配上述端口的輸入、輸出類型。

首先clk和rst都為輸入端口。cmd為我們自己設(shè)置,但是我們這里寫的不是頂層文件,cmd具體的值需要在頂層文件中設(shè)置,所以此處也為input輸入類型。同理控制IIC啟動的go端口也是在頂層文件中設(shè)置,此處為input輸入。發(fā)送數(shù)據(jù)存儲器tx_data中的數(shù)據(jù)也是在頂層文件中寫進(jìn)去,為input輸入。rx_data為接收到的數(shù)據(jù),在頂層文件中接收,接收到后這里再將其輸出來查看處理,所以為output輸出端。
其它端口信號,發(fā)送完成標(biāo)志位和應(yīng)答信號還有時鐘信號都是輸出的信號。數(shù)據(jù)線SDA的端口i2c_sda,因為其又要寫數(shù)據(jù)也要讀數(shù)據(jù),所以為inout,輸入輸出端,即可輸入又可輸出。

然后此處定義系統(tǒng)外接的50M系統(tǒng)時鐘,定義IIC的工作速度為400K(這里IIC的工作速度分三種,標(biāo)準(zhǔn)模式為100K,快速模式為400K,高速模式為3.4M),然后計算計數(shù)器計數(shù)的最大值SCL_CNT_M。

然后此處定義的便是數(shù)據(jù)線SDA的三態(tài)門輸入,當(dāng)i2c_sda_oe為1時i2c_sda輸出i2c_sda_o的值,當(dāng)i2c_sda_oe為0時i2c_sda輸出高阻抗。

此處寫的是計數(shù)器,用來書寫時鐘線SCL,當(dāng)計數(shù)值div_cnt未達(dá)到IIC工作速度的最大值時,div_cnt自加,達(dá)到最大值時則清零,該計數(shù)器由使能端en_div_cnt控制。下面的sclk_plus則是一次計數(shù)滿后置1,用作一個標(biāo)志信號。
接下來便是狀態(tài)機(jī)的書寫,首先定義如下幾個常量。

上面的一組分別為,wr為寫請求,sta為起始位請求,rd為讀請求,sto為停止位請求,ack為應(yīng)答位請求,nack為無應(yīng)答請求,當(dāng)系統(tǒng)符合相應(yīng)位請求時便會跳轉(zhuǎn)到相應(yīng)的狀態(tài)。
下面的一組數(shù)據(jù)則是對應(yīng)相應(yīng)的狀態(tài),idle為默認(rèn)初始狀態(tài),gen_sta則為產(chǎn)生起始信號狀態(tài),wr_data則為寫數(shù)據(jù)狀態(tài),rd_data則為讀數(shù)據(jù)狀態(tài),check_ack為核對應(yīng)答位信號狀態(tài),gen_ack為產(chǎn)生應(yīng)答信號狀態(tài),gen_sto為產(chǎn)生停止信號狀態(tài)。

首先看在狀態(tài)idle下的時序,首先將傳輸完成標(biāo)志信號清零,然后打開三態(tài)輸出端i2c_sda_oe置1,等待IIC開啟信號go置1,開啟信號go置1后打開計數(shù)器的使能端en_div_cnt,計數(shù)器開始為時鐘信號SCL計時。然后便開始匹配選擇有沒有起始信號,以及是寫狀態(tài)還是讀狀態(tài),當(dāng)有起始信號時,狀態(tài)機(jī)便跳轉(zhuǎn)置產(chǎn)生起始信號的狀態(tài),即gen_sta。

此處為產(chǎn)生起始信號狀態(tài),在cnt的計數(shù)值下書寫每一個信號的狀態(tài),cnt為0初始時打開三態(tài)輸入端,i2c_sda輸出i2c_sda_o的狀態(tài)高電平1,cnt為1時,將時鐘線SCL拉高為1,cnt為2時將數(shù)據(jù)線SDA拉低,產(chǎn)生下降沿,數(shù)據(jù)線SCL持續(xù)為高,如此便產(chǎn)生了起始信號,cnt為3時將SCL拉低,如此為時鐘線SCL的時鐘周期。
接下來當(dāng)cnt為3時便判斷下一個狀態(tài)為寫狀態(tài)或者讀狀態(tài)。此處需要說明一下,因為此處為時序邏輯,賦值方式采用的是非阻塞賦值,所以賦值的狀態(tài)有點特殊,即當(dāng)前賦的值都是在下一個時鐘周期才生效。

此處為讀寫狀態(tài),上面為寫狀態(tài),當(dāng)sclk_plus為1時cnt自加1,cnt計數(shù)4次為一個SCL時鐘周期,在寫數(shù)據(jù)時,一個數(shù)據(jù)為8位(二進(jìn)制),逐次將每一位數(shù)據(jù)存儲到i2c_sda_o中,然后打開三態(tài)門i2c_sda_oe,cnt加1后將SCL時鐘線拉高,cnt再加1SCL時鐘線電平保持高電平,此時根據(jù)IIC主從機(jī)協(xié)議,數(shù)據(jù)線SDA上的數(shù)據(jù)將被從機(jī)讀取,然后cnt再加1便將SCL時鐘線上的電平拉低。寫數(shù)據(jù)完成之后狀態(tài)機(jī)便跳轉(zhuǎn)到等待應(yīng)答信號的狀態(tài)check_ack。
下面的便是讀數(shù)據(jù),讀數(shù)據(jù)和寫數(shù)據(jù)類似,也是cnt逐次自加,cnt從0自加到3,中間四次便是一個SCL時鐘線的周期。首先關(guān)閉SDA的三態(tài)門,將i2c_sda_oe置0,將時鐘線scl拉低,cnt自加1之后將SCL時鐘線拉高,cnt在此自加之后SCL時鐘線保持高電平,將SDA上讀取到的數(shù)據(jù)通過移位的方式存儲到rx_data存儲器中,cnt再次自加后便將SCL時鐘線拉低。

這里便是等待應(yīng)答的狀態(tài),等待應(yīng)答的過程也相當(dāng)于等待接收1位的數(shù)據(jù),當(dāng)cnt為0時,關(guān)閉i2c_sda的三態(tài)門,將i2c_sda_oe置0,時鐘線SCL置0,cnt自加1后將SCL時鐘線拉高,cnt再次自加1之后便開始讀取SDA數(shù)據(jù)線上接收到的數(shù)據(jù),將接收到的數(shù)據(jù)賦給端口ack_o,用來判斷應(yīng)答信號是否正確,cnt再次自加之后便將SCL時鐘線拉低。cnt自加到3之后,便開始判斷當(dāng)前的IIC通信是否需要加停止位。

產(chǎn)生應(yīng)答的過程也為一次輸出的過程,cnt為0時打開三態(tài)使能端,將i2c_sda_oe置1,將時鐘線SCL拉低,然后判斷應(yīng)答信號是否發(fā)送,需要發(fā)送則i2c_sda的取值i2c_sda_o賦0,無應(yīng)答則將i2c_sda_o賦1。因為應(yīng)答信號ack為0有效。cnt自加后將時鐘線SCL拉高,cnt再次自加后時鐘線SCL保持高電平,cnt自加到3時,將時鐘線SCL拉低。cnt為3后開始判斷是否需要停止位,用來判斷下一個跳轉(zhuǎn)狀態(tài)。

根據(jù)停止位的時序,當(dāng)SCL時鐘線為高電平時,SDA數(shù)據(jù)線產(chǎn)生一個上升沿便為停止位,即當(dāng)cnt為0時打開i2c_sda的三態(tài)使能端,將i2c_sda_oe置1,將i2c_sda_o置0,這樣SDA數(shù)據(jù)線便先輸出低電平,cnt自加1后將時鐘線SCL拉高,cnt再次自加后將i2c_sda_o的值賦1,使SDA數(shù)據(jù)線輸出高電平,如此便產(chǎn)生了一個上升沿,cnt再次自加置3時,SCL數(shù)據(jù)線保持高電平,如此IIC的停止位便產(chǎn)生了。
03 IIC仿真波形
首先便是老生常談的例化該程序。

定義相匹配的寄存器,然后例化過程寫的文件,這里pullup是將引腳上拉的意思,在電路中都存在上拉電路,SDA在電路中都是被上拉電阻拉至高電平狀態(tài)。

然后此處便是產(chǎn)生50M的時鐘源。

這里還寫了task調(diào)用程序,首先寫的是寫字節(jié)的task,輸入三個數(shù)據(jù),每一個數(shù)據(jù)都是8位,然后分別仿真了有起始位、無起始位無停止位、和有停止位的過程,先將開始IIC的信號go拉高開啟,然后將需要寫入的數(shù)據(jù)賦給寄存器tx_data中保存,然后關(guān)閉go,延時一段時間后便開始捕獲發(fā)送完成信號trans_done的上升沿,下面兩組數(shù)據(jù)的方式同理。

然后便是讀取數(shù)據(jù)的task語句仿真,這里輸入兩個,同樣也是仿真是否需要起始位、停止位,然后開啟IIC開始信號go,開始時給go賦1,然后將數(shù)據(jù)存入寄存器,延時后關(guān)閉開啟信號go,然后再延時等待捕捉傳輸完成標(biāo)志的上升沿。
下面是仿真波形圖

整體波形如圖所示。接下來為發(fā)送一個數(shù)據(jù)的仿真波形圖。

首先最頭上的光標(biāo)表示的是本次發(fā)送的數(shù)據(jù)為10100000,第二個光標(biāo)處表示的是起始位,SCL為高,SDA產(chǎn)生下降沿,后面兩個光標(biāo)之間便是傳輸?shù)臄?shù)據(jù),SCL高電平時看SDA上發(fā)送的數(shù)據(jù)。

然后這個圖便是接收數(shù)據(jù)的波形圖,SCL為高電平時讀取SDA上的數(shù)據(jù),并通過移位的方式將讀取的數(shù)據(jù)存儲到rx_data寄存器中。
電子發(fā)燒友App






















評論