久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

【接口時(shí)序】6、IIC總線的原理與Verilog實(shí)現(xiàn)

 星海邊陲的天才 2019-10-17

一,、 軟件平臺(tái)與硬件平臺(tái)

軟件平臺(tái):

1,、操作系統(tǒng):Windows-8.1

2、開(kāi)發(fā)套件:ISE14.7

3,、仿真工具:ModelSim-10.4-SE ,、ChipScope

硬件平臺(tái):

1、 FPGA型號(hào):Xilinx公司的XC6SLX45-2CSG324

2,、 EEPROM型號(hào):Microchip公司的AT24LC04B

二,、 原理介紹

IIC(Inter-Integrated Circuit)總線是一種由PHILIPS公司開(kāi)發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備,。I2C總線產(chǎn)生于在80年代,,最初為音頻和視頻設(shè)備開(kāi)發(fā),如今主要在服務(wù)器管理中使用,,其中包括單個(gè)組件狀態(tài)的通信,。例如管理員可對(duì)各個(gè)組件進(jìn)行查詢,以管理系統(tǒng)的配置或掌握組件的功能狀態(tài),,如電源和系統(tǒng)風(fēng)扇,。可隨時(shí)監(jiān)控內(nèi)存,、硬盤(pán),、網(wǎng)絡(luò)、系統(tǒng)溫度等多個(gè)參數(shù),,增加了系統(tǒng)的安全性,,方便了管理。IIC數(shù)據(jù)傳輸速率有標(biāo)準(zhǔn)模式(100 kbps),、快速模式(400 kbps)和高速模式(3.4 Mbps),,另外一些變種實(shí)現(xiàn)了低速模式(10 kbps)和快速+模式(1 Mbps)。

下圖是一個(gè)嵌入式系統(tǒng)中處理器僅通過(guò)2根線的IIC總線控制多個(gè)IIC外設(shè)的典型應(yīng)用圖

圖中處理器是IIC主機(jī),,它僅僅通過(guò)兩根信號(hào)就可以控制IO擴(kuò)展器,,各種不同的傳感器,EEPROM,,AD/DAs等設(shè)備,,這也是IIC總線協(xié)議相較于其他協(xié)議最有優(yōu)勢(shì)的地方,。

IIC總線的特點(diǎn):

1、 簡(jiǎn)單性和有效性,。由于接口直接在組件之上,,因此I2C總線占用的空間非常小,,減少了電路板的空間和芯片管腳的數(shù)量,,降低了互聯(lián)成本??偩€的長(zhǎng)度可高達(dá)25英尺,,并且能夠以10Kbps的最大傳輸速率支持40個(gè)組件。

2,、 支持多主控(multimastering),, 其中任何能夠進(jìn)行發(fā)送和接收的設(shè)備都可以成為主總線。一個(gè)主控能夠控制信號(hào)的傳輸和時(shí)鐘頻率,。當(dāng)然,,在任何時(shí)間點(diǎn)上只能有一個(gè)主控占用IIC總線。

IIC總線協(xié)議詳解:

IIC總線接口是一個(gè)標(biāo)準(zhǔn)的雙向傳輸接口,,一次數(shù)據(jù)傳輸需要主機(jī)和從機(jī)按照IIC協(xié)議的標(biāo)準(zhǔn)進(jìn)行,。I2C總線是由數(shù)據(jù)線SDA和時(shí)鐘SCL構(gòu)成的串行總線,可發(fā)送和接收數(shù)據(jù),,并且在硬件上都需要接一個(gè)上拉電阻到VCC,。各種被控制電路均并聯(lián)在這條總線上,但就像電話機(jī)一樣只有撥通各自的號(hào)碼才能工作,,所以每個(gè)電路和模塊都有唯一的地址,,這樣,各控制電路雖然掛在同一條總線上,,卻彼此獨(dú)立,,互不相關(guān)。

IIC主機(jī)往從機(jī)里面寫(xiě)入數(shù)據(jù)的步驟如下:

1,、 主機(jī)發(fā)送一個(gè)起始信號(hào)和從機(jī)的設(shè)備地址給從機(jī)

2,、 主機(jī)發(fā)送數(shù)據(jù)給從機(jī)

3、 主機(jī)發(fā)送一個(gè)停止信號(hào)結(jié)束發(fā)送過(guò)程

IIC主機(jī)從從機(jī)里面讀出數(shù)據(jù)的步驟如下:

1,、 主機(jī)發(fā)送一個(gè)起始信號(hào)和從機(jī)的設(shè)備地址給從機(jī)

2,、 主機(jī)發(fā)送一個(gè)要讀取的地址給從機(jī)

3、 主機(jī)從從機(jī)接收數(shù)據(jù)

4,、 主機(jī)發(fā)送一個(gè)停止信號(hào)給從機(jī)結(jié)束整個(gè)接收過(guò)程

總的來(lái)說(shuō),,IIC總線在通信的過(guò)程中一共有一下幾種狀態(tài):

1、空閑狀態(tài)

IIC 總線的 SDA 和 SCL 兩條信號(hào)線同時(shí)處于高電平時(shí),,規(guī)定為總線的空閑狀態(tài),。此時(shí)各個(gè)器件的輸出級(jí)場(chǎng)效應(yīng)管均處在截止?fàn)顟B(tài),,即釋放總線,由兩條信號(hào)線各自的上拉電阻把電平拉高,。

2,、起始狀態(tài)和結(jié)束狀態(tài)

在時(shí)鐘線 SCL 保持高電平期間,數(shù)據(jù)線 SDA 上的電平被拉低(即負(fù)跳變),,定義為 I2C 總線總線的起始信號(hào),,它標(biāo)志著一次數(shù)據(jù)傳輸?shù)拈_(kāi)始。起始信號(hào)是由主控器主動(dòng)建立的,,在建立該信號(hào)之前 I2C 總線必須處于空閑狀態(tài),。

在時(shí)鐘線 SCL 保持高電平期間,數(shù)據(jù)線 SDA 被釋放,,使得 SDA 返回高電平(即正跳變),,稱為 I2C 總線的停止信號(hào),它標(biāo)志著一次數(shù)據(jù)傳輸?shù)慕K止,。停止信號(hào)也是由主控器主動(dòng)建立的,,建立該信號(hào)之后,I2C 總線將返回空閑狀態(tài),。

起始信號(hào)和結(jié)束信號(hào)如下圖所示

3,、有效的數(shù)據(jù)位傳輸

在 IIC 總線上傳送的每一位數(shù)據(jù)都有一個(gè)時(shí)鐘脈沖相對(duì)應(yīng)(或同步控制),,即在 SCL 串行時(shí)鐘的配合下,,數(shù)據(jù)在 SDA 上從高位向低位依次串行傳送每一位的數(shù)據(jù)。進(jìn)行數(shù)據(jù)傳送時(shí),,在 SCL 呈現(xiàn)高電平期間,,SDA 上的電平必須保持穩(wěn)定,低電平為數(shù)據(jù) 0,,高電平為數(shù)據(jù) 1,。只有在 SCL 為低電平期間,才允許 SDA 上的電平改變狀態(tài),。下圖是0xaa在IIC總線上有效傳輸(有效傳輸是指第9個(gè)時(shí)鐘的高電平期間,,從機(jī)給主機(jī)反饋了一個(gè)有效的應(yīng)答位0)的圖示

4、應(yīng)答信號(hào)與非應(yīng)答信號(hào)

I2C 總線上的所有數(shù)據(jù)都是以 8 位字節(jié)傳送的,,發(fā)送器(主機(jī))每發(fā)送一個(gè)字節(jié),,就在第9個(gè)時(shí)鐘脈沖期間釋放數(shù)據(jù)線,由接收器(從機(jī))反饋一個(gè)應(yīng)答信號(hào),。應(yīng)答信號(hào)為低電平時(shí),,規(guī)定為有效應(yīng)答位(ACK簡(jiǎn)稱應(yīng)答位),表示接收器已經(jīng)成功地接收了該字節(jié),;應(yīng)答信號(hào)為高電平時(shí),,規(guī)定為非應(yīng)答位(NACK),,一般表示接收器接收該字節(jié)沒(méi)有成功。對(duì)于反饋有效應(yīng)答位 ACK 的要求是,,接收器在第 9 個(gè)時(shí)鐘脈沖之前的低電平期間將 SDA 線拉低,,并且確保在該時(shí)鐘的高電平期間為穩(wěn)定的低電平。

對(duì)非應(yīng)答位(NACK)還要特別說(shuō)明的是,,還有以下四種情況IIC通信過(guò)程中會(huì)產(chǎn)生非應(yīng)答位:

1,、接收器(從機(jī))正在處理某些實(shí)時(shí)的操作無(wú)法與主機(jī)實(shí)現(xiàn)IIC通信的時(shí)候,接收器(從機(jī))會(huì)給主機(jī)反饋一個(gè)非應(yīng)答位(NACK)

2,、主機(jī)發(fā)送數(shù)據(jù)的過(guò)程中,,從機(jī)無(wú)法解析發(fā)送的數(shù)據(jù),接收器(從機(jī))也會(huì)給主機(jī)反饋一個(gè)非應(yīng)答位(NACK)

3,、主機(jī)發(fā)送數(shù)據(jù)的過(guò)程中,從機(jī)無(wú)法再繼續(xù)接收數(shù)據(jù),,接收器(從機(jī))也會(huì)給主機(jī)反饋一個(gè)非應(yīng)答位(NACK)

4,、主機(jī)從從機(jī)中讀取數(shù)據(jù)的過(guò)程中,主機(jī)不想再接收數(shù)據(jù),,主機(jī)會(huì)給從機(jī)反饋一個(gè)非應(yīng)答位(NACK),,注意,這種情況是主機(jī)給從機(jī)反饋一個(gè)非應(yīng)答位(NACK)

關(guān)于有效應(yīng)答位的圖示在上一傳輸0xaa的圖中可以清楚的看到,,關(guān)于非應(yīng)答位的圖示見(jiàn)下圖

了解清楚IIC總線在通信的過(guò)程中的幾種狀態(tài)以后接下來(lái)就具體看看IIC總線的讀寫(xiě)過(guò)程吧,。

1、主機(jī)通過(guò)IIC總線往從機(jī)里面寫(xiě)數(shù)據(jù)

主機(jī)通過(guò)IIC總線往從機(jī)中寫(xiě)數(shù)據(jù)的時(shí)候,,主機(jī)首先會(huì)發(fā)送一個(gè)起始信號(hào),,接著把IIC從機(jī)的7位設(shè)備地址后面添一個(gè)0(設(shè)備地址后面的0表示主機(jī)向從機(jī)寫(xiě)數(shù)據(jù),1表示主機(jī)從從機(jī)中讀數(shù)據(jù))組成一個(gè)8位的數(shù)據(jù),,把這個(gè)8位的數(shù)據(jù)發(fā)給從機(jī),,發(fā)完這8位的數(shù)據(jù)以后主機(jī)馬上釋放SDA信號(hào)線等待從機(jī)的應(yīng)答,如果從機(jī)正確收到這個(gè)數(shù)據(jù),,從機(jī)就會(huì)發(fā)送一個(gè)有效應(yīng)答位0給主機(jī)告訴主機(jī)自己已經(jīng)收到了數(shù)據(jù),,主機(jī)收到從機(jī)的有效應(yīng)答位以后 ,接下來(lái)主機(jī)會(huì)發(fā)送想要寫(xiě)入的寄存器地址,,寄存器發(fā)送完畢以后主機(jī)同樣會(huì)釋放SDA信號(hào)線等待從機(jī)的應(yīng)答,,從機(jī)如果正確收到了主機(jī)發(fā)過(guò)來(lái)的寄存器地址,從機(jī)會(huì)再次發(fā)送一個(gè)有效應(yīng)答位給主機(jī),,主機(jī)收到從機(jī)的有效應(yīng)答位0以后,,接下來(lái)主機(jī)就會(huì)給從機(jī)發(fā)送想要寫(xiě)入從機(jī)的數(shù)據(jù),從機(jī)正確收到這個(gè)數(shù)據(jù)以后仍然像之前兩次一樣會(huì)給主機(jī)發(fā)送一個(gè)有效應(yīng)答位,,主機(jī)收到這個(gè)有效應(yīng)答位以后給從機(jī)發(fā)送一個(gè)停止信號(hào),,整個(gè)傳輸過(guò)程就結(jié)束了,。下圖是整個(gè)傳輸過(guò)程的示意圖:

特別注意:上圖中灰色的地方表示主機(jī)正在控制SDA信號(hào)線,白色的地方表示從機(jī)正在控制SDA信號(hào)線,。

2,、主機(jī)通過(guò)IIC總線從從機(jī)里面讀數(shù)據(jù)

主機(jī)通過(guò)IIC總線從從機(jī)中讀數(shù)據(jù)的過(guò)程與寫(xiě)數(shù)據(jù)的過(guò)程有相似之處,但是讀數(shù)據(jù)的過(guò)程還多了一些額外的步驟,。主機(jī)從從機(jī)讀數(shù)據(jù)時(shí)主機(jī)首先會(huì)發(fā)送一個(gè)起始信號(hào),,接著把IIC從機(jī)的7位設(shè)備地址后面添一個(gè)0(設(shè)備地址后面的0表示主機(jī)向從機(jī)寫(xiě)數(shù)據(jù),1表示主機(jī)從從機(jī)中讀數(shù)據(jù)),,把這個(gè)8位的數(shù)據(jù)發(fā)給從機(jī),,發(fā)完這8位的數(shù)據(jù)以后主機(jī)馬上釋放SDA信號(hào)線等待從機(jī)的應(yīng)答,如果從機(jī)正確收到這個(gè)數(shù)據(jù),,從機(jī)就會(huì)發(fā)送一個(gè)有效應(yīng)答位0給主機(jī)告訴主機(jī)自己已經(jīng)收到了數(shù)據(jù),,主機(jī)收到從機(jī)的有效應(yīng)答位以后 ,接下來(lái)主機(jī)會(huì)發(fā)送想要讀的寄存器地址,,寄存器發(fā)送完畢以后主機(jī)同樣會(huì)釋放SDA信號(hào)線等待從機(jī)的應(yīng)答,,從機(jī)如果正確收到了主機(jī)發(fā)過(guò)來(lái)的寄存器地址,從機(jī)會(huì)再次發(fā)送一個(gè)有效應(yīng)答位給主機(jī),,主機(jī)收到從機(jī)的有效應(yīng)答位0以后,,主機(jī)會(huì)給從機(jī)再次發(fā)送一次起始信號(hào),接著把IIC從機(jī)的7位設(shè)備地址后面添一個(gè)1(設(shè)備地址后面的0表示主機(jī)向從機(jī)寫(xiě)數(shù)據(jù),,1表示主機(jī)從從機(jī)中讀數(shù)據(jù)),,注意,第一次是在設(shè)備地址后面添0,,這一次是在設(shè)備地址后面添1,,把這個(gè)8位的數(shù)據(jù)發(fā)給從機(jī),發(fā)完這8位的數(shù)據(jù)以后主機(jī)馬上釋放SDA信號(hào)線等待從機(jī)的應(yīng)答,,如果從機(jī)正確收到這個(gè)數(shù)據(jù),,從機(jī)就會(huì)發(fā)送一個(gè)有效應(yīng)答位0給主機(jī)告訴主機(jī)自己已經(jīng)收到了數(shù)據(jù),接著從機(jī)繼續(xù)占用SDA信號(hào)線給主機(jī)發(fā)送寄存器中的數(shù)據(jù),,發(fā)送完畢以后,,主機(jī)再次占用SDA信號(hào)線發(fā)送一個(gè)非應(yīng)答信號(hào)1給從機(jī),主機(jī)發(fā)送一個(gè)停止信號(hào)給從機(jī)結(jié)束整個(gè)讀數(shù)據(jù)的過(guò)程,。下圖是整個(gè)讀數(shù)據(jù)過(guò)程的示意圖

特別注意:上圖中灰色的地方表示主機(jī)正在控制SDA信號(hào)線,,白色的地方表示從機(jī)正在控制SDA信號(hào)線。

三,、 目標(biāo)任務(wù)

1,、編寫(xiě)IIC總線主機(jī)給從機(jī)發(fā)送數(shù)據(jù)的代碼,實(shí)現(xiàn)FPGA(主機(jī))往EEPROM(從機(jī))的0x23這個(gè)地址寫(xiě)入0x45這個(gè)數(shù)據(jù)

2、編寫(xiě)IIC總線主機(jī)從從機(jī)接收數(shù)據(jù)的代碼,,實(shí)現(xiàn)FPGA(主機(jī))從EEPROM(從機(jī))的0x23這個(gè)地址讀出0x45這個(gè)數(shù)據(jù),,并用0x45這個(gè)數(shù)據(jù)的低四位驅(qū)動(dòng)4個(gè)LED

四、 設(shè)計(jì)思路與Verilog代碼編寫(xiě)

4.1,、 IIC發(fā)送模塊的接口定義與整體設(shè)計(jì)

Verilog編寫(xiě)的IIC發(fā)送模塊除了進(jìn)行IIC通信的兩根信號(hào)線(SCL和SDA)以外還要包括一些時(shí)鐘,、復(fù)位、使能,、并行的輸入輸出以及完成標(biāo)志位,。其框圖如下所示

其中:

I_clk是系統(tǒng)時(shí)鐘;

I_rst_n是系統(tǒng)復(fù)位,;

I_iic_send_en發(fā)送使能信號(hào),,當(dāng)I_iic_send_en為1時(shí)IIC主機(jī)(FPGA)才能給IIC從機(jī)發(fā)送數(shù)據(jù);

I_dev_addr[6:0]是IIC從機(jī)的設(shè)備地址,;

I_word_addr[7:0]是字地址,,也就是我們想要操作的IIC設(shè)備的內(nèi)部存儲(chǔ)地址;

I_write_data[7:0]是主機(jī)(FPGA)要往IIC字地址中寫(xiě)入的數(shù)據(jù),;

O_done_flag是主機(jī)(FPGA)發(fā)送一個(gè)字節(jié)完成標(biāo)志位,,發(fā)送完成后會(huì)產(chǎn)生一個(gè)高脈沖;

O_scl是IIC總線的串行時(shí)鐘線,;

IO_sda是IIC總線的串行數(shù)據(jù)線;

要想實(shí)現(xiàn)iic_send模塊的功能,,還是先得抽象出發(fā)送一個(gè)字節(jié)數(shù)據(jù)時(shí)序的狀態(tài)機(jī),,這里把24LC04B發(fā)送過(guò)程的時(shí)序貼一遍

注意,上圖中的控制字節(jié)(CONTROL BYTE)實(shí)際上就是代碼里面定義的7-bit設(shè)備物理地址與最后1-bit的讀寫(xiě)控制位拼接組成的,。

通過(guò)觀察上面的時(shí)序圖可以看出,,發(fā)送一個(gè)字節(jié)的數(shù)據(jù)之前必須要先發(fā)送起始位,然后發(fā)送控制字節(jié),,接著等待應(yīng)答,,然后在發(fā)送字地址,接著在等待應(yīng)答,。數(shù)據(jù)發(fā)送完畢以后,,在等待最后一個(gè)應(yīng)答,應(yīng)答成功后發(fā)送停止信號(hào)結(jié)束整個(gè)過(guò)程,。所以,,根據(jù)這個(gè)流程,可以歸納出如下幾個(gè)狀態(tài):

狀態(tài)0:空閑狀態(tài),,用來(lái)初始化各個(gè)寄存器的值

狀態(tài)1:加載IIC設(shè)備的物理地址

狀態(tài)2:加載IIC設(shè)備的字地址

狀態(tài)3:加載要發(fā)送的數(shù)據(jù)

狀態(tài)4:發(fā)送起始信號(hào)

狀態(tài)5:發(fā)送一個(gè)字節(jié),,從高位開(kāi)始發(fā)送

狀態(tài)6:接收應(yīng)答狀態(tài)的應(yīng)答位

狀態(tài)7:校驗(yàn)應(yīng)答位

狀態(tài)8:發(fā)送停止信號(hào)

狀態(tài)9:IIC寫(xiě)操作結(jié)束

需要注意的是上面的各個(gè)狀態(tài)并不是按照順序執(zhí)行的,有些狀態(tài)要復(fù)用多次,比如狀態(tài)5發(fā)送字節(jié)的狀態(tài)就需要復(fù)用三次用來(lái)發(fā)送三個(gè)8-bit的數(shù)據(jù),;同樣,,狀態(tài)6和狀態(tài)7也要復(fù)用多次。

抽象出狀態(tài)機(jī)以后,,寫(xiě)代碼之前先分析一下代碼中要注意的一些關(guān)鍵點(diǎn):

1,、由于IIC時(shí)序要求數(shù)據(jù)線SDA在串行時(shí)鐘線的高電平保持不變,在串行時(shí)鐘線的低電平才能變化,,所以代碼里面必須在串行時(shí)鐘線低電平的正中間產(chǎn)生一個(gè)標(biāo)志位,,寫(xiě)代碼的時(shí)候在這個(gè)標(biāo)志位處改變SDA的值,這樣就可以保證SDA在SCL的高電平期間保持穩(wěn)定了,。同理,,由于IIC從機(jī)(24LC04)在接收到主機(jī)(FPGA)發(fā)送的有效數(shù)據(jù)以后會(huì)在SCL高電平期間產(chǎn)生一個(gè)有效應(yīng)答信號(hào)0,所以為了保證采到的應(yīng)答信號(hào)準(zhǔn)確,,必須在SCL高電平期間的正中間判斷應(yīng)答信號(hào)是否滿足條件(0為有效應(yīng)答,,1為無(wú)效應(yīng)答),因此代碼里面還必須在串行時(shí)鐘線高電平的正中間產(chǎn)生一個(gè)標(biāo)志位,,在這個(gè)標(biāo)志下接收應(yīng)答位并進(jìn)行校驗(yàn),。

這部分的代碼通過(guò)一個(gè)計(jì)數(shù)器就很容易實(shí)現(xiàn),代碼如下:

復(fù)制代碼

C_DIV_SELECT        =      C_DIV_SELECT0       =   (C_DIV_SELECT >> )  -             ,              C_DIV_SELECT1       =   (C_DIV_SELECT >> )  -             ,             C_DIV_SELECT2       =   (C_DIV_SELECT0 + C_DIV_SELECT1) +  ,              C_DIV_SELECT3       =   (C_DIV_SELECT >> )  +             ;  @( I_clk   I_rst_n)     (!I_rst_n)         R_scl_cnt   <=        (R_scl_en)                         (R_scl_cnt == C_DIV_SELECT -                  R_scl_cnt <=                               R_scl_cnt <= R_scl_cnt +                        R_scl_cnt   <=  O_scl          = (R_scl_cnt <= C_DIV_SELECT1) ? b0 ;  W_scl_low_mid  = (R_scl_cnt == C_DIV_SELECT2) ? b0 ;  W_scl_high_mid = (R_scl_cnt == C_DIV_SELECT0) ? b0 ;

復(fù)制代碼

2,、有了SCL信號(hào)低電平正中間標(biāo)志位和高電平正中間標(biāo)志位以后最好還產(chǎn)生一個(gè)下降沿的標(biāo)志位,。原因是在發(fā)送第一個(gè)8-bit數(shù)據(jù)以后,處理這個(gè)8-bit數(shù)據(jù)應(yīng)答位的位置在SCL信號(hào)高電平的正中間,,由于要復(fù)用發(fā)送8-bit數(shù)據(jù)的那個(gè)狀態(tài),,所以必須在第二次進(jìn)入發(fā)送8-bit數(shù)據(jù)的狀態(tài)時(shí)必須提前把數(shù)據(jù)再次加載好,因此可以在這個(gè)下降沿的標(biāo)志來(lái)加載第二次要發(fā)送的數(shù)據(jù),,然后在SCL下降沿的正中間把8-bit數(shù)據(jù)發(fā)出去,。這里必須結(jié)合代碼來(lái)理解,這里可以暫時(shí)有個(gè)印象,。

3,、IIC總線的SDA數(shù)據(jù)線是一個(gè)雙向IO口,關(guān)于雙向IO在Verilog代碼中如何進(jìn)行處理,,我在《QSPI Flash的原理與QSPI時(shí)序的實(shí)現(xiàn)》這篇博客已經(jīng)做了說(shuō)明,,這里不再贅述,直接給出代碼如下:

復(fù)制代碼

module Test_inout (input   I_clk,input   I_rst_n,     .     .     .inout   IO_data,     .     .     .  )reg     R_data_out  ;wire    I_data_in   ;assign  IO_data = Control ? R_data_out : 1'bz ;assign  I_data_in   =   IO_data ;always @(posedge I_clk or negedge I_rst_n)begin     .     .     .     ;endendmodule

復(fù)制代碼

4,、發(fā)送8-bit數(shù)據(jù)的整個(gè)過(guò)程如下:加載8-bit數(shù)據(jù)->發(fā)送8-bit數(shù)據(jù)->接收應(yīng)答位->校驗(yàn)應(yīng)答位->加載第二個(gè)8-bit數(shù)據(jù)……....,。所以為了復(fù)用中間標(biāo)紅的這幾個(gè)狀態(tài),必須在加載8-bit數(shù)據(jù)這個(gè)狀態(tài)提前設(shè)置好校驗(yàn)應(yīng)答位狀態(tài)執(zhí)行完畢以后的后一個(gè)狀態(tài)的位置,,這在代碼里面通過(guò)R_jump_state這個(gè)變量來(lái)完成,。這一點(diǎn)也必須對(duì)照著代碼來(lái)進(jìn)行理解。

思路理清楚以后就可以直接編寫(xiě)Verilog代碼了,iic_send模塊的代碼如下:

復(fù)制代碼

module iic_send (    input                I_clk           , // 系統(tǒng)50MHz時(shí)鐘     input                I_rst_n         , // 系統(tǒng)全局復(fù)位     input                 I_iic_send_en   , // IIC發(fā)送使能位          input        [6:0]   I_dev_addr      , // IIC設(shè)備的物理地址     input        [7:0]   I_word_addr     , // IIC設(shè)備的字地址,,即我們想操作的IIC的內(nèi)部地址     input        [7:0]   I_write_data    , // 往IIC設(shè)備的字地址寫(xiě)入的數(shù)據(jù)     output  reg          O_done_flag     , // 讀或?qū)慖IC設(shè)備結(jié)束標(biāo)志位         // 標(biāo)準(zhǔn)的IIC設(shè)備總線     output               O_scl           , // IIC總線的串行時(shí)鐘線     inout                IO_sda            // IIC總線的雙向數(shù)據(jù)線);           parameter   C_DIV_SELECT        =   10'd500 ; // 分頻系數(shù)選擇parameter   C_DIV_SELECT0       =   (C_DIV_SELECT >> 2)  -  1           , // 用來(lái)產(chǎn)生IIC總線SCL低電平最中間的標(biāo)志位             C_DIV_SELECT1       =   (C_DIV_SELECT >> 1)  -  1           ,             C_DIV_SELECT2       =   (C_DIV_SELECT0 + C_DIV_SELECT1) + 1 , // 用來(lái)產(chǎn)生IIC總線SCL高電平最中間的標(biāo)志位             C_DIV_SELECT3       =   (C_DIV_SELECT >> 1)  +  1           ; // 用來(lái)產(chǎn)生IIC總線SCL下降沿標(biāo)志位  reg     [9:0]   R_scl_cnt       ; // 用來(lái)產(chǎn)生IIC總線SCL時(shí)鐘線的計(jì)數(shù)器   reg             R_scl_en        ; // IIC總線SCL時(shí)鐘線使能信號(hào)reg     [3:0]   R_state         ;  reg             R_sda_mode      ; // 設(shè)置SDA模式,,1位輸出,0為輸入reg             R_sda_reg       ; // SDA寄存器reg     [7:0]   R_load_data     ; // 發(fā)送/接收過(guò)程中加載的數(shù)據(jù),,比如設(shè)備物理地址,,字地址和數(shù)據(jù)等reg     [3:0]   R_bit_cnt       ; // 發(fā)送字節(jié)狀態(tài)中bit個(gè)數(shù)計(jì)數(shù)reg             R_ack_flag      ; // 應(yīng)答標(biāo)志reg     [3:0]   R_jump_state    ; // 跳轉(zhuǎn)狀態(tài),傳輸一個(gè)字節(jié)成功并應(yīng)答以后通過(guò)這個(gè)變量跳轉(zhuǎn)到導(dǎo)入下一個(gè)數(shù)據(jù)的狀態(tài)wire            W_scl_low_mid   ; // SCL的低電平中間標(biāo)志位wire            W_scl_high_mid  ; // SCL的高電平中間標(biāo)志位wire            W_scl_neg        ; // SCL的下降沿標(biāo)志位assign IO_sda  =  (R_sda_mode == 1'b1) ? R_sda_reg : 1'bz ;always @(posedge I_clk or negedge I_rst_n)begin     if(!I_rst_n)         R_scl_cnt   <=  10'd0 ;      else if(R_scl_en)            begin             if(R_scl_cnt == C_DIV_SELECT - 1'b1)                 R_scl_cnt <= 10'd0 ;             else                 R_scl_cnt <= R_scl_cnt + 1'b1 ;              end     else         R_scl_cnt     <= 10'd0 ;endassign O_scl           = (R_scl_cnt <= C_DIV_SELECT1) ? 1'b1 : 1'b0 ; // 產(chǎn)生串行時(shí)鐘信號(hào)O_sclassign W_scl_low_mid  = (R_scl_cnt == C_DIV_SELECT2) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl低電平正中間標(biāo)志位assign W_scl_high_mid = (R_scl_cnt == C_DIV_SELECT0) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl高電平正中間標(biāo)志位assign W_scl_neg       = (R_scl_cnt == C_DIV_SELECT3) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl下降沿標(biāo)志位always @(posedge I_clk or negedge I_rst_n)begin     if(!I_rst_n)        begin             R_state         <=  4'd0 ;             R_sda_mode      <=  1'b1 ;             R_sda_reg       <=  1'b1 ;             R_bit_cnt       <=  4'd0 ;             O_done_flag     <=  1'b0 ;             R_jump_state    <=  4'd0 ;             R_ack_flag        <=    1'b0 ;         end     else if(I_iic_send_en) // 往IIC設(shè)備發(fā)送數(shù)據(jù)         begin             case(R_state)                4'd0   : // 空閑狀態(tài)設(shè)置SCL與SDA均為高                     begin                         R_sda_mode      <=  1'b1 ; // 設(shè)置SDA為輸出                         R_sda_reg       <=  1'b1 ; // 設(shè)置SDA為高電平                         R_scl_en        <=  1'b0 ; // 關(guān)閉SCL時(shí)鐘線                         R_state         <=  4'd1 ; // 下一個(gè)狀態(tài)是加載設(shè)備物理地址狀態(tài)                         R_bit_cnt       <=  4'd0 ; // 發(fā)送字節(jié)狀態(tài)中bit個(gè)數(shù)計(jì)數(shù)清零                         O_done_flag     <=  1'b0 ;                         R_jump_state    <=  4'd0 ;                     end                                                4'd1   :  // 加載IIC設(shè)備物理地址                                   begin                                                      R_load_data     <=  {I_dev_addr, 1'b0}  ;                         R_state         <=  4'd4                ;                         R_jump_state    <=  4'd2                ;                     end                                                      4'd2   :   // 加載IIC設(shè)備字地址                                          begin                                                            R_load_data     <=  I_word_addr         ;                          R_state         <=  4'd5                ;                         R_jump_state    <=  4'd3                ;                     end                                                      4'd3   :    // 加載要發(fā)送的數(shù)據(jù)                                         begin                                                            R_load_data     <=  I_write_data        ;                          R_state         <=  4'd5                ;                         R_jump_state    <=  4'd8                ;                     end                                                                          4'd4   :    // 發(fā)送起始信號(hào)                                        begin                                                            R_scl_en    <=  1'b1                ; // 打開(kāi)SCL時(shí)鐘線                         R_sda_mode  <=  1'b1                ; // 設(shè)置SDA為輸出                         if(W_scl_high_mid)                                               begin                                                            R_sda_reg   <=  1'b0        ; // 在SCL高電平中間把SDA信號(hào)拉低,產(chǎn)生起始信號(hào)                                 R_state     <=  4'd5        ;                              end                         else                             R_state <=  4'd4                ; // 如果SCL高電平中間標(biāo)志沒(méi)出現(xiàn)就一直在這個(gè)狀態(tài)等著                                               end                 4'd5   :    // 發(fā)送1個(gè)字節(jié),,從高位開(kāi)始發(fā)                     begin                         R_scl_en    <=  1'b1                ; // 打開(kāi)SCL時(shí)鐘線                         R_sda_mode  <=  1'b1                ; // 設(shè)置SDA為輸出                         if(W_scl_low_mid)                            begin                                 if(R_bit_cnt == 4'd8)                                     begin                                         R_bit_cnt   <=  4'd0            ;                                         R_state     <=  4'd6            ; // 字節(jié)發(fā)完以后進(jìn)入應(yīng)答狀態(tài)                                     end                                 else                                     begin                                                                          R_sda_reg   <=  R_load_data[7-R_bit_cnt] ; // 先發(fā)送高位                                         R_bit_cnt   <=  R_bit_cnt + 1'b1         ;                                      end                             end                         else                             R_state <=  4'd5 ; // 字節(jié)沒(méi)發(fā)完時(shí)在這個(gè)狀態(tài)一直等待                      end                  4'd6   :    // 接收應(yīng)答狀態(tài)的應(yīng)答位                     begin                         R_scl_en    <=  1'b1  ; // 打開(kāi)SCL時(shí)鐘線                         R_sda_mode  <=  1'b0  ; // 設(shè)置SDA為輸入                         if(W_scl_high_mid)                            begin                                 R_ack_flag  <=  IO_sda  ;                                  R_state     <=  4'd7    ;                              end                                                     else                             R_state <=  4'd6  ;                          end                 4'd7  :    // 校驗(yàn)應(yīng)答位                     begin                         R_scl_en    <=  1'b1  ; // 打開(kāi)SCL時(shí)鐘線                                                 if(R_ack_flag == 1'b0)    // 校驗(yàn)通過(guò)                             begin                                 if(W_scl_neg == 1'b1)                                      begin                                         R_state <=  R_jump_state ;                                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸出                                         R_sda_reg   <=  1'b0 ; // 讀取完應(yīng)答信號(hào)以后要把SDA信號(hào)設(shè)置成輸出并拉低,,因?yàn)槿绻@個(gè)狀                                                                // 態(tài)后面是停止?fàn)顟B(tài)的話,需要SDA信號(hào)的上升沿,,所以這里提前拉低它                                     end                                 else                                     R_state <= 4'd7    ;                             end                         else                             R_state <=  4'd0 ;                           end                 4'd8   : // 發(fā)送停止信號(hào)                     begin                         R_scl_en    <=  1'b1        ; // 打開(kāi)SCL時(shí)鐘線                         R_sda_mode  <=  1'b1        ; // 設(shè)置SDA為輸出                         if(W_scl_high_mid)                            begin                                 R_sda_reg   <=  1'b1 ;                                 R_state     <=  4'd9 ;                             end                     end                 4'd9    :   // IIC寫(xiě)操作結(jié)束                     begin                         R_scl_en    <=  1'b0 ; // 關(guān)閉SCL時(shí)鐘線                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA為輸出                         R_sda_reg   <=  1'b1 ; // 拉高SDA保持空閑狀態(tài)情況                         O_done_flag <=  1'b1 ;                         R_state     <=  4'd0 ;                          R_ack_flag  <=  1'b0 ;                     end                   default    : R_state     <=  4'd0 ;              endcase         end      else         begin             R_state         <=  4'd0 ;             R_sda_mode      <=  1'b1 ;             R_sda_reg       <=  1'b1 ;             R_bit_cnt       <=  4'd0 ;             O_done_flag     <=  1'b0 ;             R_jump_state    <=  4'd0 ;             R_ack_flag      <=  1'b0 ;         endendwire    [35:0]    CONTROL0 ;wire    [54:0]    TRIG0 ; icon icon_inst (     .CONTROL0(CONTROL0) // INOUT BUS [35:0]); ila ila_inst (     .CONTROL(CONTROL0), // INOUT BUS [35:0]     .CLK(I_clk), // IN     .TRIG0(TRIG0) // IN BUS [49:0]);assign TRIG0[0] = O_scl ;assign TRIG0[1] = IO_sda ;assign TRIG0[11:2] = R_scl_cnt ;assign TRIG0[12] = R_scl_en ;assign TRIG0[16:13] = R_state ;assign TRIG0[17] = R_sda_mode ;assign TRIG0[18] = R_sda_reg ;assign TRIG0[26:19] = R_load_data ;assign TRIG0[30:27] = R_bit_cnt ;assign TRIG0[31] = R_ack_flag ;assign TRIG0[36:32] = R_jump_state ;assign TRIG0[37] = W_scl_low_mid ;assign TRIG0[38] = W_scl_high_mid ;assign TRIG0[39] = O_done_flag ;assign TRIG0[40] = I_rst_n ;endmodule

復(fù)制代碼

整個(gè)代碼的流程與之前分析的流程完全一致,。本來(lái)想寫(xiě)一個(gè)測(cè)試文件用ModelSim進(jìn)行基本的仿真,但是由于應(yīng)答信號(hào)是取決于IIC從設(shè)備的,,所以還是決定用ChipScope直接抓,。在用ChipScope抓之前先寫(xiě)一個(gè)頂層文件把上面的代碼例化進(jìn)去,頂層代碼如下:

復(fù)制代碼

module iic_send_top (    input           I_clk           , // 系統(tǒng)50MHz時(shí)鐘     input           I_rst_n         , // 系統(tǒng)全局復(fù)位         // 標(biāo)準(zhǔn)的IIC設(shè)備總線     output          O_scl           , // IIC總線的串行時(shí)鐘線     inout           IO_sda            // IIC總線的雙向數(shù)據(jù)線);wire             W_done_flag ; iic_send U_iic_send (     .I_clk           (I_clk            ), // 系統(tǒng)50MHz時(shí)鐘     .I_rst_n         (I_rst_n          ), // 系統(tǒng)全局復(fù)位     .I_iic_send_en   (1'b1             ), // 發(fā)送使能位,,高電平有效          .I_dev_addr      (7'b1010_000      ), // IIC設(shè)備的物理地址     .I_word_addr     (8'h23            ), // IIC設(shè)備的字地址,,即我們想操作的IIC的內(nèi)部地址     .I_write_data    (8'h45            ), // 往IIC設(shè)備的字地址寫(xiě)入的數(shù)據(jù)      .O_done_flag     (W_done_flag      ), // 讀或?qū)慖IC設(shè)備結(jié)束標(biāo)志位         // 標(biāo)準(zhǔn)的IIC設(shè)備總線     .O_scl           (O_scl            ), // IIC總線的串行時(shí)鐘線     .IO_sda          (IO_sda           )  // IIC總線的雙向數(shù)據(jù)線);endmodule

復(fù)制代碼

綁定好管腳以后就可以生成bit文件下載到FPGA里面用ChipScope抓時(shí)序了,下面是我抓到的時(shí)序圖:

為了更清晰的說(shuō)明上面的時(shí)序,,我把起始信號(hào),,停止信號(hào),每個(gè)比特以及應(yīng)答位全部框出來(lái)進(jìn)一步解釋如下:

通過(guò)上面的時(shí)序圖可以清楚的看到:

1號(hào)紅框是起始信號(hào),,在SCL高電平期間SDA有一個(gè)下降沿

2~9號(hào)紅框是發(fā)送設(shè)備物理地址8’ha0(8’b1010_0000)

10號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,SDA為輸入

11~18號(hào)紅框是發(fā)送字地址8’h23(8’b0010_0011)

19號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,SDA為輸入

20~27號(hào)紅框是發(fā)送數(shù)據(jù)8’h45(8’b0100_0101)

28號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,,SDA為輸入

29號(hào)紅框是停止信號(hào),在SCL高電平期間SDA有一個(gè)上升沿

其他變量的時(shí)序細(xì)節(jié)這里不再展開(kāi),,大家可以自己抓出來(lái),。至此,IIC發(fā)送模塊全部設(shè)計(jì)完畢,。

4.2,、 IIC接收模塊的接口定義與整體設(shè)計(jì)

Verilog編寫(xiě)的IIC接收模塊除了進(jìn)行IIC通信的兩根信號(hào)線(SCL和SDA)以外還要包括一些時(shí)鐘、復(fù)位,、使能,、并行的輸入輸出以及完成標(biāo)志位。其框圖如下所示

其中:

I_clk是系統(tǒng)時(shí)鐘;

I_rst_n是系統(tǒng)復(fù)位,;

I_iic_recv_en接收使能信號(hào),,當(dāng)I_iic_recv_en為1時(shí)IIC主機(jī)(FPGA)才能從IIC從機(jī)接收數(shù)據(jù);

I_dev_addr[6:0]是IIC從機(jī)的設(shè)備地址,;

I_word_addr[7:0]是字地址,,也就是我們想要讀取的IIC設(shè)備的內(nèi)部存儲(chǔ)地址;

O_read_data[7:0]是主機(jī)(FPGA)從IIC設(shè)備字地址中讀取的數(shù)據(jù),;

O_done_flag是主機(jī)(FPGA)接收一個(gè)字節(jié)完成標(biāo)志位,,接收完成后會(huì)產(chǎn)生一個(gè)高脈沖;

O_scl是IIC總線的串行時(shí)鐘線,;

IO_sda是IIC總線的串行數(shù)據(jù)線,;

要想實(shí)現(xiàn)iic_send模塊的功能,還是先得抽象出發(fā)送一個(gè)字節(jié)數(shù)據(jù)時(shí)序的狀態(tài)機(jī),,這里把24LC04B接收過(guò)程的時(shí)序貼一遍

注意,,上圖中的控制字節(jié)(CONTROL BYTE)實(shí)際上就是代碼里面定義的7-bit設(shè)備物理地址與最后1-bit讀寫(xiě)控制位組成的。

通過(guò)觀察上面的時(shí)序圖可以看出,,接收一個(gè)字節(jié)的數(shù)據(jù)的過(guò)程與發(fā)送一個(gè)字節(jié)數(shù)據(jù)相比多了一個(gè)第二次的起始信號(hào)與控制字節(jié)(CONTROL BYTE),,而且第二個(gè)控制字節(jié)(CONTROL BYTE)的最低位應(yīng)該為1,表示IIC主機(jī)(FPGA)從IIC從機(jī)(24LC04)中讀數(shù)據(jù),,當(dāng)主機(jī)(FPGA)想結(jié)束讀數(shù)據(jù)的過(guò)程時(shí),,它會(huì)給IIC設(shè)備發(fā)送一個(gè)非應(yīng)答位1,最后在發(fā)送停止信號(hào)結(jié)束整個(gè)讀數(shù)據(jù)的過(guò)程,。所以,,根據(jù)這個(gè)流程,可以歸納出如下幾個(gè)狀態(tài):

狀態(tài)0:空閑狀態(tài),,用來(lái)初始化各個(gè)寄存器的值

狀態(tài)1:加載IIC設(shè)備的物理地址

狀態(tài)2:加載IIC設(shè)備的字地址

狀態(tài)3:發(fā)送第一個(gè)起始信號(hào)(讀過(guò)程要求發(fā)送兩次起始信號(hào))

狀態(tài)4:發(fā)送一個(gè)字節(jié)數(shù)據(jù),,從高位開(kāi)始發(fā)送

狀態(tài)5:接收應(yīng)答狀態(tài)的應(yīng)答位

狀態(tài)6:校驗(yàn)應(yīng)答位

狀態(tài)7:發(fā)送第二個(gè)起始信號(hào)(讀過(guò)程要求發(fā)送兩次起始信號(hào)

狀態(tài)8:再次加載IIC設(shè)備的物理地址,但這次物理地址最后一位應(yīng)該為1,,表示讀操作

狀態(tài)9:接收一個(gè)字節(jié)數(shù)據(jù),,從高位開(kāi)始接收

狀態(tài)10:主機(jī)發(fā)送一個(gè)非應(yīng)答信號(hào)1給從機(jī)

狀態(tài)11:等確定從機(jī)收到這個(gè)非應(yīng)答信號(hào)1以后,初始化SDA的值為0,,準(zhǔn)備產(chǎn)生停止信號(hào)

狀態(tài)12:發(fā)送停止信號(hào)

狀態(tài)13:讀操作結(jié)束

需要注意的是上面的各個(gè)狀態(tài)和發(fā)送模塊一樣,,并不是按照順序執(zhí)行的,有些狀態(tài)也要復(fù)用多次,。

接收模塊有以下幾個(gè)關(guān)鍵點(diǎn)要注意:

1,、和發(fā)送模塊一樣,需要產(chǎn)生SCL信號(hào)高電平中間標(biāo)志位,,低電平中間標(biāo)志位以及下降沿標(biāo)志位

2,、由于讀數(shù)據(jù)的過(guò)程需要發(fā)送第二次起始位,,而起始位的條件是在SCL高電平期間SDA有一個(gè)下降沿,所以一定要在處理完寫(xiě)設(shè)備地址與寫(xiě)字地址的應(yīng)答位之后,,在SCL的下降沿標(biāo)志處把SDA信號(hào)設(shè)置成輸出并拉高方便產(chǎn)生第二次起始信號(hào),。具體細(xì)節(jié)對(duì)照著代碼理解。

3,、第一次發(fā)送的設(shè)備物理地址的最低位是0,,表示寫(xiě)數(shù)據(jù);第二次發(fā)送的設(shè)備物理地址的最低位是1,,表示讀數(shù)據(jù)

4,、讀完一個(gè)字節(jié)數(shù)據(jù)以后,一定要記住是主機(jī)(FPGA)給從機(jī)(24LC04)發(fā)送一個(gè)非應(yīng)答信號(hào)1

有了上面這些儲(chǔ)備以后就可以編寫(xiě)接收模塊的代碼了,,接收模塊的代碼如下:

復(fù)制代碼

module iic_recv (    input                I_clk           , // 系統(tǒng)50MHz時(shí)鐘     input                I_rst_n         , // 系統(tǒng)全局復(fù)位     input                I_iic_recv_en   , // IIC發(fā)送使能位          input        [6:0]   I_dev_addr      , // IIC設(shè)備的物理地址     input        [7:0]   I_word_addr     , // IIC設(shè)備的字地址,,即我們想操作的IIC的內(nèi)部地址     output  reg  [7:0]   O_read_data     , // 從IIC設(shè)備的字地址讀出來(lái)的數(shù)據(jù)        output  reg          O_done_flag     , // 讀或?qū)慖IC設(shè)備結(jié)束標(biāo)志位         // 標(biāo)準(zhǔn)的IIC設(shè)備總線     output               O_scl           , // IIC總線的串行時(shí)鐘線     inout                IO_sda            // IIC總線的雙向數(shù)據(jù)線);           parameter   C_DIV_SELECT        =   10'd500 ; // 分頻系數(shù)選擇parameter   C_DIV_SELECT0       =   (C_DIV_SELECT >> 2)  -  1           , // 用來(lái)產(chǎn)生IIC總線SCL低電平最中間的標(biāo)志位             C_DIV_SELECT1       =   (C_DIV_SELECT >> 1)  -  1           , // 用來(lái)產(chǎn)生IIC串行時(shí)鐘線             C_DIV_SELECT2       =   (C_DIV_SELECT0 + C_DIV_SELECT1) + 1 , // 用來(lái)產(chǎn)生IIC總線SCL高電平最中間的標(biāo)志位             C_DIV_SELECT3       =   (C_DIV_SELECT >> 1) + 1             ; // 用來(lái)產(chǎn)生IIC總線SCL下降沿標(biāo)志位  reg     [9:0]   R_scl_cnt       ; // 用來(lái)產(chǎn)生IIC總線SCL時(shí)鐘線的計(jì)數(shù)器   reg             R_scl_en        ; // IIC總線SCL時(shí)鐘線使能信號(hào)reg     [3:0]   R_state         ;  reg             R_sda_mode      ; // 設(shè)置SDA模式,1位輸出,,0為輸入reg             R_sda_reg       ; // SDA寄存器reg     [7:0]   R_load_data     ; // 發(fā)送/接收過(guò)程中加載的數(shù)據(jù),,比如設(shè)備物理地址,字地址和數(shù)據(jù)等reg     [3:0]   R_bit_cnt       ; // 發(fā)送字節(jié)狀態(tài)中bit個(gè)數(shù)計(jì)數(shù)reg             R_ack_flag      ; // 應(yīng)答標(biāo)志reg     [3:0]   R_jump_state    ; // 跳轉(zhuǎn)狀態(tài),,傳輸一個(gè)字節(jié)成功并應(yīng)答以后通過(guò)這個(gè)變量跳轉(zhuǎn)到導(dǎo)入下一個(gè)數(shù)據(jù)的狀態(tài)reg     [7:0]   R_read_data_reg ;wire            W_scl_low_mid   ; // SCL的低電平中間標(biāo)志位wire            W_scl_high_mid  ; // SCL的高電平中間標(biāo)志位assign IO_sda  =  (R_sda_mode == 1'b1) ? R_sda_reg : 1'bz ;always @(posedge I_clk or negedge I_rst_n)begin     if(!I_rst_n)         R_scl_cnt   <=  10'd0 ;      else if(R_scl_en)            begin             if(R_scl_cnt == C_DIV_SELECT - 1'b1)                 R_scl_cnt <= 10'd0 ;             else                 R_scl_cnt <= R_scl_cnt + 1'b1 ;              end     else         R_scl_cnt     <= 10'd0 ;endassign O_scl           = (R_scl_cnt <= C_DIV_SELECT1) ? 1'b1 : 1'b0 ; // 產(chǎn)生串行時(shí)鐘信號(hào)O_sclassign W_scl_low_mid  = (R_scl_cnt == C_DIV_SELECT2) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl低電平正中間標(biāo)志位assign W_scl_high_mid = (R_scl_cnt == C_DIV_SELECT0) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl高電平正中間標(biāo)志位assign W_scl_neg       = (R_scl_cnt == C_DIV_SELECT3) ? 1'b1 : 1'b0 ; // 產(chǎn)生scl下降沿標(biāo)志位always @(posedge I_clk or negedge I_rst_n)begin     if(!I_rst_n)        begin             R_state         <=  4'd0 ;             R_sda_mode      <=  1'b1 ;             R_sda_reg       <=  1'b1 ;             R_bit_cnt       <=  4'd0 ;             O_done_flag     <=  1'b0 ;             R_jump_state    <=  4'd0 ;             R_read_data_reg <=  8'd0 ;             R_ack_flag        <=    1'b0 ;             O_read_data        <=    8'd0 ;         end     else if(I_iic_recv_en) // 往IIC設(shè)備發(fā)送數(shù)據(jù)         begin             case(R_state)                4'd0    :   // 空閑狀態(tài),,用來(lái)初始化相關(guān)所有信號(hào)                     begin                         R_sda_mode      <=  1'b1 ; // 設(shè)置SDA為輸出                         R_sda_reg       <=  1'b1 ; // 設(shè)置SDA為高電平                         R_scl_en        <=  1'b0 ; // 關(guān)閉SCL時(shí)鐘線                         R_state         <=  4'd1 ; // 下一個(gè)狀態(tài)是加載設(shè)備物理地址狀態(tài)                         R_bit_cnt       <=  4'd0 ;                         O_done_flag     <=  1'b0 ;                         R_jump_state    <=  5'd0 ;                         R_read_data_reg <=  8'd0 ;                     end                 4'd1    :   // 加載IIC設(shè)備物理地址                      begin                         R_load_data <=  {I_dev_addr, 1'b0}  ;                         R_state     <=  4'd3                ; // 加載完設(shè)備物理地址以后進(jìn)入起始狀態(tài)                         R_jump_state <=  R_state + 1'b1     ;                      end                 4'd2   :   // 加載IIC設(shè)備字地址                                          begin                                                            R_load_data <=  I_word_addr         ;                          R_state     <=  4'd4                ;                         R_jump_state <=  R_state + 5'd5      ; // 設(shè)置這里是為了這一輪發(fā)送并應(yīng)答后跳到第二次啟始位                     end                  4'd3    :   // 發(fā)送第一個(gè)起始信號(hào)                     begin                         R_scl_en    <=  1'b1                ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1                ; // 設(shè)置SDA的模式為輸出                         if(W_scl_high_mid)                            begin                                 R_sda_reg   <=  1'b0        ; // 在SCL高電平的正中間把SDA引腳拉低產(chǎn)生一個(gè)下降沿                                 R_state     <=  4'd4        ; // 下一個(gè)狀態(tài)是發(fā)送一個(gè)字節(jié)數(shù)據(jù)(IIC設(shè)備的物理地址)                              end                         else                             R_state <=  4'd3                ;                         end                                       4'd4    :   // 發(fā)送一個(gè)字節(jié)                     begin                         R_scl_en    <=  1'b1                ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1                ; // 設(shè)置SDA的模式為輸出                         if(W_scl_low_mid)                     // 在SCL低電平的最中間改變數(shù)據(jù)                             begin                                 if(R_bit_cnt == 4'd8)                                     begin                                         R_bit_cnt  <=  4'd0 ;                                           R_state    <=  4'd5 ;                                     end                                  else                                     begin                                         R_sda_reg  <=  R_load_data[7-R_bit_cnt] ;                                         R_bit_cnt  <=  R_bit_cnt + 1'b1        ;                                      end                                  end                         else                             R_state <=  4'd4    ;                         end                 4'd5    :   // 接收應(yīng)答狀態(tài)應(yīng)答位                     begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_reg   <=  1'b0 ;                         R_sda_mode  <=  1'b0 ; // 設(shè)置SDA的模式為輸入                         if(W_scl_high_mid)                               begin                                 R_ack_flag  <=  IO_sda  ;                                 R_state     <=  4'd6    ;                                                                end                         else                             R_state <=  4'd5    ;                         end                                       4'd6    :   // 校驗(yàn)應(yīng)答位                     begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                          if(R_ack_flag   == 1'b0)    // 校驗(yàn)通過(guò)                             begin                                 if(W_scl_neg == 1'b1)                                     begin                                         R_state <=  R_jump_state ;                                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸出                                         R_sda_reg   <=  1'b1 ; // 設(shè)置SDA的引腳電平拉高,方便后面產(chǎn)生第二次起始位                                     end                                 else                                     R_state <= 4'd6    ;                             end                         else                             R_state <=  4'd0 ;                         end                  4'd7    :   // 第二次起始位(IIC讀操作要求有2次起始位)                      begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸出                          if(W_scl_high_mid)                            begin                                 R_sda_reg   <=  1'b0  ;                                 R_state     <=  4'd8  ;                              end                         else                             R_state <=  4'd7 ;                         end                  4'd8   :   // 再次加載IIC設(shè)備物理地址 ,,但這次地址最后一位應(yīng)該為1,,表示讀操作                                         begin                                                            R_load_data     <=  {I_dev_addr, 1'b1}  ; // 前7bit是設(shè)備物理地址,最后一位1表示讀操作                         R_state         <=  4'd4            ;                         R_jump_state    <=  4'd9            ; // 設(shè)置這里是為了這一輪發(fā)送并應(yīng)答后跳到第二次啟始位                     end                 4'd9    :   // 讀一個(gè)字節(jié)數(shù)據(jù)                     begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b0 ; // 設(shè)置SDA的模式為輸入                         if(W_scl_high_mid)                            begin                                 if(R_bit_cnt == 4'd7)                                     begin                                         R_bit_cnt    <=  4'd0    ;                                         R_state      <=  4'd10   ;                                           O_read_data  <=  {R_read_data_reg[6:0],IO_sda}  ;                                                                                end                                 else                                     begin                                         R_read_data_reg  <=  {R_read_data_reg[6:0],IO_sda}  ;                                         R_bit_cnt        <=  R_bit_cnt   +   1'b1            ;                                       end                                 end                          else                             R_state <=  4'd9 ;                         end                  4'd10    :  // 讀完一個(gè)字節(jié)數(shù)據(jù)以后進(jìn)入10,,主機(jī)發(fā)送一個(gè)非應(yīng)答信號(hào)1                     begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸入                         if(W_scl_low_mid)                            begin                                 R_state     <=  4'd11   ;                                  R_sda_reg    <=    1'b1     ;                             end                          else                             R_state <=  4'd10 ;                         end                  4'd11   :                       begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸入                         if(W_scl_low_mid)                            begin                                 R_state     <=  4'd12   ;                                  R_sda_reg    <=    1'b0     ;                             end                         else                             R_state <=  4'd11   ;                         end                  4'd12   : //停止位Stop                     begin                         R_scl_en    <=  1'b1 ; // 打開(kāi)時(shí)鐘                         R_sda_mode  <=  1'b1 ; // 設(shè)置SDA的模式為輸出                         if(W_scl_high_mid)                            begin                                 R_sda_reg   <=  1'b1    ;                                 R_state     <=  4'd13   ;                              end                         else                             R_state <=  4'd12   ;                         end                  4'd13   :                     begin                         R_scl_en        <=  1'b0 ; // 關(guān)閉SCL時(shí)鐘線                         R_sda_mode      <=  1'b1 ; // 設(shè)置SDA為輸出                         R_sda_reg       <=  1'b1 ; // 拉高SDA保持空閑狀態(tài)情況                         O_done_flag     <=  1'b1 ;                         R_state         <=  4'd0 ;                         R_read_data_reg <=  8'd0 ;                     end                  default: R_state         <=  4'd0 ;             endcase         end      else         begin             R_state         <=  4'd0 ;             R_sda_mode      <=  1'b1 ;             R_sda_reg       <=  1'b1 ;             R_bit_cnt       <=  4'd0 ;             O_done_flag     <=  1'b0 ;             R_jump_state    <=  4'd0 ;             R_read_data_reg <=  8'd0 ;             R_ack_flag         <=  1'b0 ;         endendwire    [35:0]    CONTROL0 ;wire    [54:0]    TRIG0 ; icon icon_inst (     .CONTROL0(CONTROL0) // INOUT BUS [35:0]); ila ila_inst (     .CONTROL(CONTROL0), // INOUT BUS [35:0]     .CLK(I_clk), // IN     .TRIG0(TRIG0) // IN BUS [49:0]);assign TRIG0[0] = O_scl ;assign TRIG0[1] = IO_sda ;assign TRIG0[11:2] = R_scl_cnt ;assign TRIG0[12] = R_scl_en ;assign TRIG0[16:13] = R_state ;assign TRIG0[17] = R_sda_mode ;assign TRIG0[18] = R_sda_reg ;assign TRIG0[26:19] = R_load_data ;assign TRIG0[30:27] = R_bit_cnt ;assign TRIG0[31] = R_ack_flag ;assign TRIG0[36:32] = R_jump_state ;assign TRIG0[37] = W_scl_low_mid ;assign TRIG0[38] = W_scl_high_mid ;assign TRIG0[39] = O_done_flag ;assign TRIG0[40] = I_rst_n ;assign TRIG0[48:41] = O_read_data ;assign TRIG0[49] = W_scl_neg ;endmodule

復(fù)制代碼

整個(gè)代碼的流程與之前分析的流程完全一致,。在用ChipScope抓之前先寫(xiě)一個(gè)頂層文件把上面的代碼例化進(jìn)去,頂層代碼如下:

復(fù)制代碼

module iic_recv_top (    input           I_clk           , // 系統(tǒng)50MHz時(shí)鐘     input           I_rst_n         , // 系統(tǒng)全局復(fù)位     output  [3:0]   O_led_out       , // 從IIC設(shè)備的字地址讀出來(lái)的數(shù)據(jù)           // 標(biāo)準(zhǔn)的IIC設(shè)備總線     output          O_scl           , // IIC總線的串行時(shí)鐘線     inout           IO_sda            // IIC總線的雙向數(shù)據(jù)線);wire              W_done_flag ;wire  [7:0]       W_read_data ; // 從IIC設(shè)備的字地址讀出來(lái)的數(shù)據(jù) assign    O_led_out = W_read_data[3:0] ; iic_recv U_iic_recv (     .I_clk           (I_clk            ), // 系統(tǒng)50MHz時(shí)鐘     .I_rst_n         (I_rst_n          ), // 系統(tǒng)全局復(fù)位     .I_iic_recv_en   (1'b1             ), // 接收使能位,,高電平有效          .I_dev_addr      (7'b1010_000     ), // IIC設(shè)備的物理地址     .I_word_addr     (8'h23            ), // IIC設(shè)備的字地址,,即我們想操作的IIC的內(nèi)部地址     .O_read_data     (W_read_data      ), // 從IIC設(shè)備的字地址讀出來(lái)的數(shù)據(jù)        .O_done_flag     (W_done_flag      ), // 讀或?qū)慖IC設(shè)備結(jié)束標(biāo)志位         // 標(biāo)準(zhǔn)的IIC設(shè)備總線     .O_scl           (O_scl            ), // IIC總線的串行時(shí)鐘線     .IO_sda          (IO_sda           )  // IIC總線的雙向數(shù)據(jù)線);endmodule

復(fù)制代碼

綁定好管腳以后就可以生成bit文件下載到FPGA里面用ChipScope抓時(shí)序了,由于EEPROM是一種非易失性存儲(chǔ)器,,所以做在IIC發(fā)送數(shù)據(jù)的實(shí)驗(yàn)中往24LC04的0x23地址中的0x45這個(gè)數(shù)據(jù)在掉電以后并不會(huì)丟失,。剛好可以通過(guò)這個(gè)接收模塊給讀出來(lái),并用讀出數(shù)據(jù)的最低位驅(qū)動(dòng)四個(gè)LED燈,,如果時(shí)序正確的話,四個(gè)LED燈會(huì)間隔亮起來(lái),。下面是我抓到的接收數(shù)據(jù)時(shí)序圖:

通過(guò)上面的時(shí)序圖可以清楚的看到成功讀出了EEPROM中的0x45這個(gè)數(shù)據(jù),,并且我板子上的四個(gè)LED燈也間隔亮了起來(lái)。

為了更清晰的說(shuō)明上面的時(shí)序,,我把起始信號(hào),,停止信號(hào),,每個(gè)比特,應(yīng)答位和非應(yīng)答位全部框出來(lái)進(jìn)一步解釋如下:

1號(hào)紅框是起始信號(hào),,在SCL高電平期間SDA有一個(gè)下降沿

2~9號(hào)紅框是發(fā)送設(shè)備物理地址8’ha0(8’b1010_0000)

10號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,SDA為輸入

11~18號(hào)紅框是發(fā)送字地址8’h23(8’b0010_0011)

19號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,,SDA為輸入

20號(hào)紅框是第二次起始位,在SCL高電平期間SDA有一個(gè)下降沿

21~28號(hào)紅框是發(fā)送數(shù)據(jù)8’ha1(8’b1010_0001)

29號(hào)紅框是應(yīng)答位,,在這個(gè)期間R_sda_mode保持低電平,,SDA為輸入

30~37號(hào)紅框是讀出的8-bit數(shù)據(jù)8’h45(8’b0100_0101),在這個(gè)期間R_sda_mode保持低電平,,SDA為輸入

38號(hào)紅框是非應(yīng)答位,,在這個(gè)期間R_sda_mode保持高電平,主機(jī)(FPGA)通過(guò)SDA輸出一個(gè)非應(yīng)答位1

39號(hào)紅框是停止信號(hào),,在SCL高電平期間SDA有一個(gè)上升沿

其他變量的時(shí)序細(xì)節(jié)這里不再展開(kāi),,大家可以自己抓出來(lái)。至此,,IIC接收模塊全部設(shè)計(jì)完畢,。

五、 進(jìn)一步思考

5.1,、 24LC04寫(xiě)數(shù)據(jù)操作要注意的地方

Following the start condition from the master, the device code (4 bits), the block address (3 bits), and the R/W bit which is a logic low is placed onto the bus by the master transmitter. This indicates to the addressed slave receiver that a byte with a word address will follow after it has generated an acknowledge bit during the ninth clock cycle. Therefore the next byte transmitted by the master is the word address and will be written into the address pointer of the 24LC04B/08B. After receiving another acknowledge signal from the 24LC04B/08B the master device will transmit the data word to be written into the addressed memory location.The 24LC04B/08B acknowledges again and the master generates a stop condition. This initiates the internal write cycle, and during this time the 24LC04B/08B will not generate acknowledge signals,。

這是24LC04芯片手冊(cè)對(duì)它的寫(xiě)操作的描述, 所以我們寫(xiě)進(jìn)去的數(shù)據(jù)其實(shí)是放在24LC04的一個(gè)緩沖區(qū)中,,等主機(jī)(FPGA)發(fā)送停止信號(hào)以后24LC04內(nèi)部才開(kāi)始工作把緩沖區(qū)中的數(shù)據(jù)寫(xiě)入它內(nèi)部的ROM中,,在這個(gè)過(guò)程中24LC04將不發(fā)送有效應(yīng)答信號(hào),所以當(dāng)發(fā)送完停止信號(hào)又立馬給一個(gè)起始信號(hào)重新發(fā)送時(shí)會(huì)出現(xiàn)下面的時(shí)序

這種情況由于24LC04內(nèi)部還在處理緩沖區(qū)中的數(shù)據(jù),,所以即使主機(jī)(FPGA)發(fā)送了正確的時(shí)序,,從機(jī)(24LC04)也不會(huì)有效應(yīng)答。

5.2,、 IIC設(shè)備多字節(jié)連續(xù)讀寫(xiě)操作

24LC04支持16-Bytes的連續(xù)寫(xiě)操作,,當(dāng)超過(guò)16-Bytes是后面寫(xiě)入的數(shù)據(jù)會(huì)覆蓋先前寫(xiě)入的數(shù)據(jù),下面是關(guān)于這一段的描述:

The write control byte, word address and the first data byte are transmitted to the 24LC04B/08B in the same way as in a byte write. But instead of generating a stop condition the master transmits up to 16 data bytes to the 24LC04B/08B which are temporarily stored in the on-chip page buffer and will be written into the memory after the master has transmitted a stop condition. After the receipt of each word, the four lower order address pointer bits are internally incremented by one. The higher order seven bits of the word address remains constant. If the master should transmit more than 16 words prior to generating the stop condition, the address counter will roll over and the previously received data will be overwritten. As with the byte write operation, once the stop condition is received an internal write cycle will begin.

時(shí)序圖如下所示:

其實(shí)要實(shí)現(xiàn)這個(gè)時(shí)序并不是難事,,只要多增加幾個(gè)加載數(shù)據(jù)的狀態(tài)就可以了,,大家可以直接在上面發(fā)送數(shù)據(jù)模塊的基礎(chǔ)上改。

24LC04支持整塊存儲(chǔ)器的連續(xù)讀操作,,下面是關(guān)于這一段的描述:

Sequential reads are initiated in the same way as a random read except that after the 24LC04B/08B transmits the first data byte, the master issues an acknowledge as opposed to a stop condition in a random read. This directs the 24LC04B/08B to transmit the next sequentially addressed 8-bit word (Figure 7-3).To provide sequential reads the 24LC04B/08B contains an internal address pointer which is incremented by one at the completion of each operation. This address pointer allows the entire memory contents to be serially read during one operation.

時(shí)序圖如下所示:

有了上面接收模塊的基礎(chǔ),,實(shí)現(xiàn)這段時(shí)序應(yīng)該也不算困難。以后有空再實(shí)現(xiàn),。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多