加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 一、I2C 總線概述
    • 二、模擬 I2C 接口程序的基本框架
    • 三、I2C?協(xié)議的具體實(shí)現(xiàn)
    • 四、程序的仿真與測(cè)試
    • 五、總結(jié)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

基于FPGA的模擬 I2C協(xié)議設(shè)計(jì)(附代碼)

07/22 14:10
5468
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

今天給大俠帶來基于FPGA的 模擬 I2C 協(xié)議設(shè)計(jì),包括?I2C 總線解析以及模擬?I2C 接口程序的基本框架、I2C?協(xié)議的具體實(shí)現(xiàn)、程序的仿真與測(cè)試。篇幅較長(zhǎng),話不多說,上貨。

之前也有相關(guān)文章介紹,僅供各位大俠參考。

導(dǎo)讀

I2C(Inter-Integrated Circuit),其實(shí)是 I2C Bus簡(jiǎn)稱,中文就是集成電路總線,它是一種串行通信總線,使用多主從架構(gòu),由飛利浦公司在1980年代為了讓主板、嵌入式系統(tǒng)或手機(jī)用以連接低速周邊設(shè)備而發(fā)展。I2C的正確讀法為“I平方C”("I-squared-C"),而“I二C”("I-two-C")則是另一種錯(cuò)誤但被廣泛使用的讀法。自2006年10月1日起,使用 I2C 協(xié)議已經(jīng)不需要支付專利費(fèi),但制造商仍然需要付費(fèi)以獲取 I2C 從屬設(shè)備地址。

I2C?簡(jiǎn)單來說,就是一種串行通信協(xié)議,I2C的通信協(xié)議和通信接口在很多工程中有廣泛的應(yīng)用,如數(shù)據(jù)采集領(lǐng)域的串行 AD,圖像處理領(lǐng)域的攝像頭配置,工業(yè)控制領(lǐng)域的 X 射線管配置等等。除此之外,由于 I2C 協(xié)議占用的 IO 資源特別少,連接方便,所以工程中也常選用 I2C 接口做為不同芯片間的通信協(xié)議。I2C 串行總線一般有兩根信號(hào)線,一根是雙向的數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL。所有接到?I2C 總線設(shè)備上的串行數(shù)據(jù)SDA都接到總線的SDA上,各設(shè)備的時(shí)鐘線SCL接到總線的SCL上。

在現(xiàn)代電子系統(tǒng)中,有為數(shù)眾多的 IC 需要進(jìn)行相互之間以及與外界的通信。為了簡(jiǎn)化電路的設(shè)計(jì),Philips 公司開發(fā)了一種用于內(nèi)部 IC 控制的簡(jiǎn)單的雙向兩線串行總線 I2C(Intel-Integrated Circuit bus)。1998 年當(dāng)推出?I2C?總線協(xié)議 2.0 版本時(shí),I2C 協(xié)議實(shí)際上已經(jīng)成為一個(gè)國(guó)際標(biāo)準(zhǔn)。

在進(jìn)行 FPGA 設(shè)計(jì)時(shí),經(jīng)常需要和外圍提供?I2C?接口的芯片通信。例如低功耗CMOS 實(shí)時(shí)時(shí)鐘/日歷芯片 PCF8563、LCD 驅(qū)動(dòng)芯片 PCF8562、并行口擴(kuò)展芯片 PCF8574、鍵盤/LED 驅(qū)動(dòng)器 ZLG7290 等都提供?I2C?接口。因此在 FPGA 中模擬?I2C?接口已成為 FPGA 開發(fā)必要的步驟。

本篇將詳細(xì)講解在 FPGA 芯片中使用 VHDL/Verilog HDL 模擬?I2C?協(xié)議,以及編寫 TestBench仿真和測(cè)試程序的方法。

第一篇內(nèi)容摘要:本篇會(huì)介紹?I2C 總線解析,包括?I2C 總線概述、I2C 協(xié)議的基本概念、I2C協(xié)議的時(shí)序要求,還會(huì)介紹模擬 I2C 接口程序的基本框架等相關(guān)內(nèi)容。

一、I2C 總線概述

下面先對(duì) I2C 協(xié)議中有關(guān)數(shù)據(jù)格式和時(shí)序的內(nèi)容進(jìn)行介紹,這里沒有涉及的地方請(qǐng)參考《THE I2C-BUS SPECIFICATION VERSION 2.1 JANUARY 2000》。

1.1?I2C?總線概述

I2C 協(xié)議作為一個(gè)串行總線標(biāo)準(zhǔn)盡管沒有并行總線的數(shù)據(jù)吞吐能力,但是它的以下特點(diǎn)使其有著廣泛的應(yīng)用:

? 只需要兩條總線—串行數(shù)據(jù)線 SDA 和串行時(shí)鐘線 SCL;

? 每個(gè)連接到總線的器件都可以通過惟一的地址和一直存在的簡(jiǎn)單的主/從節(jié)點(diǎn)關(guān)系軟件設(shè)定地址,主節(jié)點(diǎn)可以發(fā)送數(shù)據(jù)或接收數(shù)據(jù);

? 是真正的多主總線,當(dāng)兩個(gè)或更多主節(jié)點(diǎn)同時(shí)初始化數(shù)據(jù)傳輸時(shí),可以通過沖突檢測(cè)和仲裁防止數(shù)據(jù)被破壞;

? 串行的 8 位雙向數(shù)據(jù)傳輸位速率在標(biāo)準(zhǔn)模式下可達(dá) 100kbit/s,快速模式下可達(dá)400kbit/s,高速模式下可達(dá) 3.4Mbit/s;

? 片上的濾波器可以濾去總線數(shù)據(jù)線上的毛刺波,保證數(shù)據(jù)完整;

? 連接到相同總線的 IC 數(shù)量只受到總線的最大電容(400pF)限制。

總線不僅僅是互連的線,還包含系統(tǒng)通信的所有格式和過程。I2C 總線結(jié)構(gòu)上的特點(diǎn)保證了其應(yīng)用時(shí)的簡(jiǎn)潔,另外其完備的協(xié)議避免了所有混亂、數(shù)據(jù)丟失和妨礙信息的可能性。

1.2 I2C?協(xié)議的基本概念

I2C 總線支持任何 IC 生產(chǎn)過程(NMOS、CMOS 和雙極性)。串行數(shù)據(jù)線 SDA 和串行時(shí)鐘線 SCL在連接到總線的器件間傳遞信息。每個(gè)器件都有一個(gè)惟一的地址作為識(shí)別的標(biāo)志(無論是微控制器、LCD 驅(qū)動(dòng)器存儲(chǔ)器還是鍵盤接口),并且都可以發(fā)送數(shù)據(jù)和接收數(shù)據(jù)。很明顯 LCD 驅(qū)動(dòng)器只需要接收數(shù)據(jù),而存儲(chǔ)器需要接收和發(fā)送數(shù)據(jù)。圖 1 所示的是一個(gè)高性能集成電視的例子。

圖 1 高性能集成電視

從圖 1 可以看到,應(yīng)用?I2C 總線是非常方便的。用通俗的話講 I2C 總線的硬件設(shè)計(jì)工作就是連接 SDA 和 SCL 兩條線,依靠 I2C 協(xié)議完成軟件工作。在 I2C 協(xié)議中應(yīng)理解如下的概念。

1)主/從節(jié)點(diǎn)

主節(jié)點(diǎn)負(fù)責(zé)初始化總線的數(shù)據(jù)傳輸,并產(chǎn)生允許傳輸?shù)臅r(shí)鐘信號(hào)。此時(shí)任何被尋址的器件都被認(rèn)為是從節(jié)點(diǎn)。當(dāng)有多個(gè)主節(jié)點(diǎn)在總線上傳輸數(shù)據(jù)時(shí),每個(gè)主節(jié)點(diǎn)產(chǎn)生自己的時(shí)鐘信號(hào)。掛接到總線上的所有外圍器件、外設(shè)接口都是總線上的節(jié)點(diǎn)。

2)總線上節(jié)點(diǎn)的尋址方式

在任何時(shí)刻總線上只有一個(gè)主控器件(主節(jié)點(diǎn))實(shí)現(xiàn)總線的控制操作,對(duì)總線上的其他節(jié)點(diǎn)尋址,可分時(shí)實(shí)現(xiàn)點(diǎn)-點(diǎn)的數(shù)據(jù)傳送。因此總線上每個(gè)節(jié)點(diǎn)都有一個(gè)固定的節(jié)點(diǎn)地址。

I2C 總線上主節(jié)點(diǎn)的地址由軟件給定,此地址存放在 I2C 總線的地址寄存器中。I2C 總線上所有的外圍器件都有規(guī)范的器件地址。器件地址由 7 位數(shù)字組成,它和 1 位方向位構(gòu)成了 I2C 總線器件的尋址字節(jié) SLA(Slave address)。

器件地址是 I2C 總線外圍接口器件固有的地址編碼,器件出廠時(shí)就已給定。數(shù)據(jù)方向位規(guī)定了總線上主節(jié)點(diǎn)對(duì)從節(jié)點(diǎn)的數(shù)據(jù)傳送方向。

1.3 I2C協(xié)議的時(shí)序要求

1)總線上的數(shù)據(jù)傳遞時(shí)序

I2C 總線上數(shù)據(jù)傳遞時(shí)序如圖 2 所示,具體步驟如下。

圖 2 I2C 總線的數(shù)據(jù)傳遞時(shí)序

? 首先主節(jié)點(diǎn)器件發(fā)送一個(gè)起始信號(hào)。

? 接下來主節(jié)點(diǎn)器件發(fā)送從節(jié)點(diǎn)地址和讀寫方式,一共 8 位。其中從節(jié)點(diǎn)地址 7 位,讀寫方式 1 位。

? 與傳輸?shù)刂芬恢碌膹墓?jié)點(diǎn)器件應(yīng)答(即 ACK)。

? 開始數(shù)據(jù)傳輸,傳輸數(shù)據(jù)數(shù)量不限。每個(gè)字節(jié)(八位)后面跟接收數(shù)據(jù)方的應(yīng)答位。例如主節(jié)點(diǎn)器件讀取從節(jié)點(diǎn)數(shù)據(jù),從節(jié)點(diǎn)發(fā)送數(shù)據(jù),主節(jié)點(diǎn)應(yīng)答;主節(jié)點(diǎn)器件寫數(shù)據(jù)到從節(jié)點(diǎn),主節(jié)點(diǎn)發(fā)送數(shù)據(jù),從節(jié)點(diǎn)應(yīng)答。

? 數(shù)據(jù)傳輸結(jié)束,主節(jié)點(diǎn)器件發(fā)送一個(gè)終止信號(hào)結(jié)束整個(gè)過程。

采用 I2C 總線后對(duì)傳送的字節(jié)數(shù)沒有限制,只要求每傳送一個(gè)字節(jié)后對(duì)方回應(yīng)一個(gè)應(yīng)答位。在發(fā)送時(shí)首先發(fā)送的是數(shù)據(jù)的最高位(MSB,Most Significant Bit)。每次傳送開始有起始信號(hào),結(jié)束時(shí)有停止信號(hào)。在總線傳送完一個(gè)字節(jié)后,可以通過對(duì)時(shí)鐘線(SCL)的控制使傳送暫停。例如當(dāng)某個(gè)外圍器件接收 N 個(gè)字節(jié)數(shù)據(jù)后需要一段處理時(shí)間以便繼續(xù)接收以后的字節(jié)數(shù)據(jù),這時(shí)可在應(yīng)答信號(hào)后使 SCL 變?yōu)榈?a class="article-link" target="_blank" href="/baike/1465710.html">電平控制總線暫停。如果主節(jié)點(diǎn)要求總線暫停也可使時(shí)鐘線保持低電平控制總線暫停。

2)總線上的時(shí)序信號(hào)

I2C 總線為同步傳輸總線,總線信號(hào)完全與時(shí)鐘同步。I2C 總線上與數(shù)據(jù)傳送有關(guān)的信號(hào)有起始信號(hào) S、終止信號(hào) P、應(yīng)答信號(hào) A 以及位傳送信號(hào)。下面將對(duì)這些信號(hào)一一介紹。

(1)起始信號(hào)

起始信號(hào)(Start Condition)如圖 3 所示。當(dāng)時(shí)鐘線 SCL 為高電平時(shí),數(shù)據(jù)線 SDA 從高電平向低電平變化將形成起始信號(hào),啟動(dòng) I2C 總線。

(2)終止信號(hào)

終止信號(hào)(Stop Condition)如圖 3 所示。當(dāng)時(shí)鐘線 SCL 為高電平時(shí),數(shù)據(jù)線 SDA 從低電平向高電平變化將形成終止信號(hào),停止 I2C 總線。

(3)應(yīng)答信號(hào)

如圖 3 所中 ACK 第 9 個(gè)時(shí)鐘脈沖對(duì)應(yīng)應(yīng)答位,相應(yīng)數(shù)據(jù)線上低電平時(shí)為應(yīng)答信號(hào),高電平時(shí)為非應(yīng)答信號(hào)。

圖 3 起始信號(hào)和終止信號(hào)

(4)位傳送信號(hào)

在 I2C 總線啟動(dòng)后或應(yīng)答信號(hào)后的第 1~8 個(gè)時(shí)鐘脈沖對(duì)應(yīng)于一個(gè)字節(jié)的 8 位數(shù)據(jù)傳送。脈沖高電平期間,數(shù)據(jù)串行傳送;低電平期間為數(shù)據(jù)準(zhǔn)備,允許總線上數(shù)據(jù)電平變換。

二、模擬 I2C 接口程序的基本框架

模擬 I2C 接口程序的基本框架如圖 4 所示。

圖 4 模擬 I2C 接口程序的基本框架

1)程序接口

用于和應(yīng)用程序連接的接口,將應(yīng)用程序的數(shù)據(jù)按照 I2C 協(xié)議的方式通過 SDA 傳遞給外部器件。包括下列內(nèi)容:

? clk_I FPGA 外部時(shí)鐘信號(hào)。

? rst_I 同步重起信號(hào)。

? arst_I 異步重起信號(hào)。

? adr_I 從節(jié)點(diǎn)地址。

? dat_I 輸入數(shù)據(jù)。

? dat_o 輸出數(shù)據(jù)。

? we_I 寫有效信號(hào)。

? stb_I 接口有效信號(hào)。

? cyc_I 有效總線周期輸入。

? ack_o 應(yīng)答信號(hào)輸出。

? inta_o 中斷信號(hào)輸出。

2)時(shí)鐘設(shè)置寄存器

I2C 協(xié)議提供了 3 種速度模式:正常速度模式 100kbit/s、快速模式 400kbit/s、高速模式3.5Mbit/s。SCL 輸出的時(shí)鐘信號(hào)頻率和速度模式一致。程序內(nèi)部使用 5 倍 SCL 信號(hào)作為時(shí)鐘,而 FPGA 外部時(shí)鐘需要經(jīng)過分頻得到程序內(nèi)部使用的時(shí)鐘。

例如:采用正常速度 100kbit/s,F(xiàn)PGA 外部時(shí)鐘為 50MHz,則時(shí)鐘設(shè)置寄存器需要設(shè)置為(50MHz/5*100kHz – 1=99)。

3)時(shí)鐘產(chǎn)生模塊

時(shí)鐘產(chǎn)生模塊產(chǎn)生 4 倍 SCL 頻率的時(shí)鐘信號(hào),它為位傳輸控制模塊中所有同步動(dòng)作提供觸發(fā)信號(hào)。

4)命令寄存器

命令寄存器共 8 位,它決定是否在總線上產(chǎn)生各種時(shí)序信號(hào)、是否讀/寫數(shù)據(jù),各位表示的含義如表 1 所示。

表 1 命令寄存器內(nèi)容

5)狀態(tài)寄存器

狀態(tài)寄存器用來顯示當(dāng)前總線的狀態(tài),例如是否接收到從節(jié)點(diǎn)的應(yīng)答信號(hào)、是否忙、是否在傳遞數(shù)據(jù)等,具體內(nèi)容如表 2 所示。

表 2 狀態(tài)寄存器內(nèi)容

6)數(shù)據(jù)傳輸寄存器

數(shù)據(jù)傳輸寄存器用于保存等待傳輸?shù)臄?shù)據(jù)。當(dāng)傳遞從節(jié)點(diǎn)地址信息時(shí),前 7 位保存從節(jié)點(diǎn)地址,最后一位保存讀寫命令;當(dāng)傳遞普通數(shù)據(jù)時(shí),8 位保存一個(gè)字節(jié)數(shù)據(jù)。數(shù)據(jù)傳輸寄存器具體內(nèi)容如表 3 所示。

表 3 數(shù)據(jù)傳輸寄存器內(nèi)容

7)數(shù)據(jù)接收寄存器

數(shù)據(jù)接收寄存器用于保存通過 I2C 總線接收到的最后一個(gè)字節(jié)內(nèi)容,具體內(nèi)容如表4所示。

表 4 數(shù)據(jù)接收寄存器內(nèi)容

8)字節(jié)傳輸控制模塊

字節(jié)傳輸控制模塊以字節(jié)為單位控制 I2C 總線的數(shù)據(jù)傳輸。這個(gè)模塊按照命令寄存器設(shè)置的內(nèi)容將數(shù)據(jù)傳輸寄存器內(nèi)容傳遞到 I2C 總線的接收端,或者從 I2C 總線發(fā)送端接收數(shù)據(jù)并保存到數(shù)據(jù)接收寄存器中。

9)位傳輸控制模塊

位傳輸控制模塊以位為單位進(jìn)行 I2C 總線的數(shù)據(jù)傳輸和產(chǎn)生各個(gè) I2C 協(xié)議命令(如開始、停止、重復(fù)開始等)。字節(jié)傳輸控制模塊控制位傳輸控制模塊的各種動(dòng)作。例如讀取一個(gè)字節(jié)數(shù)據(jù),位傳輸控制模塊需要執(zhí)行 8 個(gè)讀的命令。

10)數(shù)據(jù)移位寄存器

數(shù)據(jù)移位寄存器保存的數(shù)據(jù)總是和當(dāng)前的數(shù)據(jù)傳輸相關(guān)的。例如在進(jìn)行讀操作時(shí),主節(jié)點(diǎn)通過移位寄存器依次通過 SDA 獲得來自 I2C 發(fā)送端的數(shù)據(jù),完成后數(shù)據(jù)拷貝到數(shù)據(jù)接收寄存器中。在寫操作時(shí),數(shù)據(jù)傳輸寄存器中的數(shù)據(jù)拷貝到數(shù)據(jù)移位寄存器中,然后依次通過 SDA 將數(shù)據(jù)傳輸?shù)?I2C 總線的接收端。

三、I2C?協(xié)議的具體實(shí)現(xiàn)

FPGA 設(shè)計(jì)一般按照從頂向下的模式進(jìn)行:首先設(shè)計(jì)芯片功能,規(guī)劃各個(gè)模塊功能;然后按照規(guī)劃實(shí)現(xiàn)各個(gè)模塊。本篇由 3 個(gè)代碼文件組成:i2c_master_bit_ctrl.v 完成位傳輸?shù)墓δ?、i2c_master_byte_ctrl.v 完成字節(jié)傳輸?shù)墓δ?、i2c_master_top.v 完成整個(gè)程序的控制功能,并提供給外部程序的接口。在 ISE 中創(chuàng)建一個(gè)項(xiàng)目,然后加入上面 3 個(gè)文件。下面依次介紹 3 個(gè)文件的內(nèi)容。本篇講解采用 Verilog HDL。

3.1 位傳輸?shù)膶?shí)現(xiàn)

i2c_master_bit_ctrl.v 完成位傳輸?shù)墓δ?。位傳輸?shù)墓δ馨〝?shù)據(jù)按位傳輸?shù)膶?shí)現(xiàn)和 I2C協(xié)議各個(gè)命令的實(shí)現(xiàn)兩部分。

如圖 5 所示開始和重復(fù)開始命令的產(chǎn)生包括 5 個(gè)階段:idle 和 A、B、C、D 等。停止命令包括 4 個(gè)階段:idle 和 A、B、C 等。讀、寫一個(gè)字節(jié)通過 8 次位操作完成。

圖?5 位傳輸完成數(shù)據(jù)的傳輸和各個(gè)命令的實(shí)現(xiàn)

實(shí)現(xiàn)代碼如下:

`include "timescale.v"`include "i2c_master_defines.v"
//模塊名稱及 IOmodule i2c_master_bit_ctrl(        clk, rst, nReset,        clk_cnt, ena, cmd, cmd_ack, busy, al, din, dout,        scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen    );        // 輸入、輸出    input clk;    input rst;    input nReset;    input ena; // 模塊使能信號(hào)    input [15:0] clk_cnt; // 時(shí)鐘分頻系數(shù)    input [3:0] cmd;    output cmd_ack; // 命令完成應(yīng)答    reg cmd_ack;    output busy; // 總線忙    reg busy;    output al; // 總線仲裁丟失    reg al;    input din;    output dout;    reg dout;    ????//?I2C?連線????input?scl_i;?//?I2C?時(shí)鐘輸入????output?scl_o;?//?I2C?時(shí)鐘輸出????output?scl_oen;?//?I2C?時(shí)鐘輸出使能    reg scl_oen;????input?sda_i;?//I2C?數(shù)據(jù)輸入????output?sda_o;?//?I2C?數(shù)據(jù)輸出????output?sda_oen;?//?I2C?數(shù)據(jù)輸出使能    reg sda_oen;        // variable declarations    reg sSCL, sSDA; // 同步后的 SCL 和 SDA 輸入    reg dscl_oen; // 延遲后的 scl_oen    reg sda_chk; // 檢 查 后 的 SDA output (Multi-master    arbitration)    reg clk_en; // 時(shí)鐘產(chǎn)生信號(hào)    wire slave_wait;    reg [15:0] cnt; // 時(shí)鐘分頻計(jì)數(shù)器        // 模塊主體    // 當(dāng)從節(jié)點(diǎn)沒有準(zhǔn)備好時(shí),下拉 SCL 來延遲周期    // 延遲 scl_oen    always @(posedge clk)        dscl_oen <= #1 scl_oen;        assign slave_wait = dscl_oen && !sSCL;        // 產(chǎn)生時(shí)鐘使能信號(hào)    always @(posedge clk or negedge nReset)        if(~nReset)            begin                cnt <= #1 16'h0;                clk_en <= #1 1'b1;            end        else if (rst)            begin                cnt <= #1 16'h0;                clk_en <= #1 1'b1;            end        else if ( ~|cnt || ~ena)            if (~slave_wait)                begin                    cnt <= #1 clk_cnt;                    clk_en <= #1 1'b1;                end            else                begin                    cnt <= #1 cnt;                    clk_en <= #1 1'b0;                end            else                begin                    cnt <= #1 cnt - 16'h1;                    clk_en <= #1 1'b0;                end                    // 產(chǎn)生總線狀態(tài)控制信號(hào)    reg dSCL, dSDA;    reg sta_condition;    reg sto_condition;        // 同步 SCL 和 SDA 輸入信號(hào),減少不穩(wěn)定風(fēng)險(xiǎn)    always @(posedge clk or negedge nReset)        if (~nReset)            begin                sSCL <= #1 1'b1;                sSDA <= #1 1'b1;                dSCL <= #1 1'b1;                dSDA <= #1 1'b1;            end        else if (rst)            begin                sSCL <= #1 1'b1;                sSDA <= #1 1'b1;                dSCL <= #1 1'b1;                dSDA <= #1 1'b1;            end        else            begin                sSCL <= #1 scl_i;                sSDA <= #1 sda_i;                dSCL <= #1 sSCL;                dSDA <= #1 sSDA;            end            // SCL 處于高時(shí)檢測(cè)到 SDA 的下降沿,即檢測(cè)開始狀態(tài)信號(hào)    // SCL 處于高時(shí)檢測(cè)到 SDA 的上升沿,即檢測(cè)停止?fàn)顟B(tài)信號(hào)    always @(posedge clk or negedge nReset)        if (~nReset)            begin                sta_condition <= #1 1'b0;                sto_condition <= #1 1'b0;            end        else if (rst)            begin                sta_condition <= #1 1'b0;                sto_condition <= #1 1'b0;            end        else            begin                sta_condition <= #1 ~sSDA & dSDA & sSCL;                sto_condition <= #1 sSDA & ~dSDA & sSCL;            end        //?產(chǎn)生?I2C?總線忙信號(hào)    always @(posedge clk or negedge nReset)        if(!nReset)            busy <= #1 1'b0;        else if (rst)            busy <= #1 1'b0;        else            busy <= #1 (sta_condition | busy) & ~sto_condition;       // 產(chǎn)生仲裁丟失信號(hào) generate arbitration lost signal    // 仲裁丟失發(fā)生在:    //?1)?主節(jié)點(diǎn)驅(qū)動(dòng)?SDA?處于高,但是?I2C?總線一直處于低    // 2) 沒有請(qǐng)求時(shí)卻檢測(cè)到停止?fàn)顟B(tài)信號(hào)    reg cmd_stop, dcmd_stop;        always @(posedge clk or negedge nReset)        if (~nReset)            begin                cmd_stop <= #1 1'b0;                dcmd_stop <= #1 1'b0;                al <= #1 1'b0;            end        else if (rst)            begin                cmd_stop <= #1 1'b0;                dcmd_stop <= #1 1'b0;                al <= #1 1'b0;            end        else            begin                cmd_stop <= #1 cmd == `I2C_CMD_STOP;                dcmd_stop <= #1 cmd_stop;                al <= #1 (sda_chk & ~sSDA & sda_oen) | (sto_condition & ~dcmd_stop);            end                // 產(chǎn)生數(shù)據(jù)輸出信號(hào),在 SCL 信號(hào)的上升沿保存 SDA    always @(posedge clk)        if(sSCL & ~dSCL)            dout <= #1 sSDA;                // 產(chǎn)生狀態(tài)機(jī)    // 狀態(tài)譯碼    parameter [16:0] idle = 17'b0_0000_0000_0000_0000;    parameter [16:0] start_a = 17'b0_0000_0000_0000_0001;    parameter [16:0] start_b = 17'b0_0000_0000_0000_0010;    parameter [16:0] start_c = 17'b0_0000_0000_0000_0100;    parameter [16:0] start_d = 17'b0_0000_0000_0000_1000;    parameter [16:0] start_e = 17'b0_0000_0000_0001_0000;    parameter [16:0] stop_a = 17'b0_0000_0000_0010_0000;    parameter [16:0] stop_b = 17'b0_0000_0000_0100_0000;    parameter [16:0] stop_c = 17'b0_0000_0000_1000_0000;    parameter [16:0] stop_d = 17'b0_0000_0001_0000_0000;    parameter [16:0] rd_a = 17'b0_0000_0010_0000_0000;    parameter [16:0] rd_b = 17'b0_0000_0100_0000_0000;    parameter [16:0] rd_c = 17'b0_0000_1000_0000_0000;    parameter [16:0] rd_d = 17'b0_0001_0000_0000_0000;    parameter [16:0] wr_a = 17'b0_0010_0000_0000_0000;    parameter [16:0] wr_b = 17'b0_0100_0000_0000_0000;    parameter [16:0] wr_c = 17'b0_1000_0000_0000_0000;    parameter [16:0] wr_d = 17'b1_0000_0000_0000_0000;    reg [16:0] c_state;        //狀態(tài)機(jī)    always @(posedge clk or negedge nReset)        if (!nReset)            begin                c_state <= #1 idle;                cmd_ack <= #1 1'b0;                scl_oen <= #1 1'b1;                sda_oen <= #1 1'b1;                sda_chk <= #1 1'b0;            end        else if (rst | al)            begin                c_state <= #1 idle;                cmd_ack <= #1 1'b0;                scl_oen <= #1 1'b1;                sda_oen <= #1 1'b1;                sda_chk <= #1 1'b0;            end        else            begin                cmd_ack <= #1 1'b0;                if (clk_en)                    case (c_state)                        // idle 狀態(tài)                        idle:                        begin                        case (cmd)                            `I2C_CMD_START:                              c_state <= #1 start_a;                            `I2C_CMD_STOP:                              c_state <= #1 stop_a;                            `I2C_CMD_WRITE:                              c_state <= #1 wr_a;                            `I2C_CMD_READ:                              c_state <= #1 rd_a;                            default:                              c_state <= #1 idle;                        endcase                        scl_oen <= #1 scl_oen; // 保持 SCL 在同一狀態(tài)                        sda_oen <= #1 sda_oen; // 保持 SDA 在同一狀態(tài)                        sda_chk <= #1 1'b0; // 不檢查 SDA 輸出                    end                    // 開始狀態(tài)                    start_a:                    begin                        c_state <= #1 start_b;                        scl_oen <= #1 scl_oen; // 保持 SCL 在同一狀態(tài)                        sda_oen <= #1 1'b1; // 保持 SDA 處于高                        sda_chk <= #1 1'b0; // 不檢查 SDA 的輸出                    end                                        start_b:                    begin                        c_state <= #1 start_c;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b1;                        sda_chk <= #1 1'b0;                    end                                        start_c:                    begin                        c_state <= #1 start_d;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b0;                        sda_chk <= #1 1'b0;                    end                                        start_d:                    begin                        c_state <= #1 start_e;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b0;                        sda_chk <= #1 1'b0;                    end                                        start_e:                    begin                    c_state <= #1 idle;                    cmd_ack <= #1 1'b1;                    scl_oen <= #1 1'b0;                    sda_oen <= #1 1'b0;                    sda_chk <= #1 1'b0;                    end                                        // 停止?fàn)顟B(tài)                    stop_a:                    begin                        c_state <= #1 stop_b;                        scl_oen <= #1 1'b0;                        sda_oen <= #1 1'b0;                        sda_chk <= #1 1'b0;                    end                                        stop_b:                    begin                        c_state <= #1 stop_c;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b0;                        sda_chk <= #1 1'b0;                    end                                        stop_c:                    begin                        c_state <= #1 stop_d;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b0;                        sda_chk <= #1 1'b0;                    end                                        stop_d:                    begin                        c_state <= #1 idle;                        cmd_ack <= #1 1'b1;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b1;                        sda_chk <= #1 1'b0;                    end                                        // 讀狀態(tài)                    rd_a:                    begin                        c_state <= #1 rd_b;                        scl_oen <= #1 1'b0; //保持 SCL 處于低                        sda_oen <= #1 1'b1; // SDA 處于三態(tài)                        sda_chk <= #1 1'b0; // 不檢查 SDA 輸出                    end                                        rd_b:                    begin                        c_state <= #1 rd_c;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b1;????????????????????????sda_chk?<=?#1?1'b0;?                    end                                        rd_c:                    begin                        c_state <= #1 rd_d;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 1'b1;                        sda_chk <= #1 1'b0;                    end                                        rd_d:                    begin                        c_state <= #1 idle;                        cmd_ack <= #1 1'b1;                        scl_oen <= #1 1'b0;                        sda_oen <= #1 1'b1;                        sda_chk <= #1 1'b0;                    end                                        // 寫狀態(tài)                    wr_a:                    begin                        c_state <= #1 wr_b;                        scl_oen <= #1 1'b0;                        sda_oen <= #1 din;                        sda_chk <= #1 1'b0;                    end                                        wr_b:                    begin                        c_state <= #1 wr_c;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 din;                        sda_chk <= #1 1'b1;                    end                                        wr_c:                    begin                        c_state <= #1 wr_d;                        scl_oen <= #1 1'b1;                        sda_oen <= #1 din;                        sda_chk <= #1 1'b1;                    end                                        wr_d:                    begin                        c_state <= #1 idle;                        cmd_ack <= #1 1'b1;                        scl_oen <= #1 1'b0;                        sda_oen <= #1 din;                        sda_chk <= #1 1'b0;                    end                                    endcase            end                    // 分配 SCL 和 SDA 輸出一直處于低        assign scl_o = 1'b0;        assign sda_o = 1'        endmodule

3.2 字節(jié)傳輸?shù)膶?shí)現(xiàn)

字節(jié)傳輸?shù)木唧w實(shí)現(xiàn)流程如圖 6 所示。

圖 6 字節(jié)傳輸控制模塊流程圖

字節(jié)傳輸控制模塊控制以字節(jié)為單位的數(shù)據(jù)傳輸。它根據(jù)命令寄存器的設(shè)置將數(shù)據(jù)傳輸寄存器中的內(nèi)容傳輸?shù)酵獠抗?jié)點(diǎn),將外部節(jié)點(diǎn)的數(shù)據(jù)接收到數(shù)據(jù)接收寄存器中。

實(shí)現(xiàn)代碼如下:

`include "timescale.v"`include "i2c_master_defines.v"//模塊module i2c_master_byte_ctrl (    clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din,    cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen );
    // 輸入、輸出    input clk; // 主時(shí)鐘    input rst; // 同步 RESET,高有效    input nReset; // 異步 RESET,低有效    input ena; // 模塊使能信號(hào)    input [15:0] clk_cnt; // 4 倍 SCL 信號(hào)        // 控制信號(hào)輸入    input start;    input stop;    input read;    input write;    input ack_in;    input [7:0] din;        // 狀態(tài)信號(hào)輸出    output cmd_ack;    reg cmd_ack;    output ack_out;    reg ack_out;    output i2c_busy;    output i2c_al;    output [7:0] dout;    ????//?I2C?信號(hào)    input scl_i;    output scl_o;    output scl_oen;    input sda_i;    output sda_o;    output sda_oen;        // 變量申明    // 狀態(tài)機(jī)    parameter [4:0] ST_IDLE = 5'b0_0000;    parameter [4:0] ST_START = 5'b0_0001;    parameter [4:0] ST_READ = 5'b0_0010;    parameter [4:0] ST_WRITE = 5'b0_0100;    parameter [4:0] ST_ACK = 5'b0_1000;    parameter [4:0] ST_STOP = 5'b1_0000;        // 位控制模塊的信號(hào)    reg [3:0] core_cmd;    reg core_txd;    wire core_ack, core_rxd;        // 移位寄存器信號(hào)    reg [7:0] sr; //8 位移位寄存器    reg shift, ld;        // 狀態(tài)機(jī)信號(hào)    wire go;    reg [2:0] dcnt;    wire cnt_done;        // 模塊主體    // 連接位控制模塊    i2c_master_bit_ctrl bit_controller (            .clk ( clk ),            .rst ( rst ),            .nReset ( nReset ),            .ena ( ena ),            .clk_cnt ( clk_cnt ),            .cmd ( core_cmd ),            .cmd_ack ( core_ack ),            .busy ( i2c_busy ),            .al ( i2c_al ),            .din ( core_txd ),            .dout ( core_rxd ),            .scl_i ( scl_i ),            .scl_o ( scl_o ),            .scl_oen ( scl_oen ),            .sda_i ( sda_i ),            .sda_o ( sda_o ),            .sda_oen ( sda_oen )????????);????????    // 產(chǎn)生 GO 信號(hào),當(dāng)讀/寫/停止/應(yīng)答時(shí)發(fā)生    assign go = (read | write | stop) & ~cmd_ack;        // 分配輸出到移位寄存器    assign dout = sr;        // 產(chǎn)生移位寄存器    always @(posedge clk or negedge nReset)        if (!nReset)            sr <= #1 8'h0;        else if (rst)            sr <= #1 8'h0;        else if (ld)            sr <= #1 din;        else if (shift)            sr <= #1 {sr[6:0], core_rxd};        // 產(chǎn)生計(jì)數(shù)器    always @(posedge clk or negedge nReset)        if (!nReset)            dcnt <= #1 3'h0;        else if (rst)            dcnt <= #1 3'h0;        else if (ld)            dcnt <= #1 3'h7;        else if (shift)            dcnt <= #1 dcnt - 3'h1;            assign cnt_done = ~(|dcnt);            // 狀態(tài)機(jī)    reg [4:0] c_state;    always @(posedge clk or negedge nReset)        if (!nReset)            begin                core_cmd <= #1 `I2C_CMD_NOP;                core_txd <= #1 1'b0;                shift <= #1 1'b0;                ld <= #1 1'b0;                cmd_ack <= #1 1'b0;                c_state <= #1 ST_IDLE;                ack_out <= #1 1'b0;            end        else if (rst | i2c_al)            begin                core_cmd <= #1 `I2C_CMD_NOP;                core_txd <= #1 1'b0;                shift <= #1 1'b0;                ld <= #1 1'b0;                cmd_ack <= #1 1'b0;                c_state <= #1 ST_IDLE;                ack_out <= #1 1'b0;            end        else            begin                // 初始化所有信號(hào)                core_txd <= #1 sr[7];                shift <= #1 1'b0;                ld <= #1 1'b0;                cmd_ack <= #1 1'b0;                case (c_state)                    //IDLE 狀態(tài)                    ST_IDLE:                        if (go)                            begin                                if (start)                                    begin                                        c_state <= #1 ST_START;                                        core_cmd <= #1 `I2C_CMD_START;                                    end                                else if (read)

                                    begin                                        c_state <= #1 ST_READ;                                        core_cmd <= #1 `I2C_CMD_READ;                                    end                                else if (write)                                    begin                                        c_state <= #1 ST_WRITE;                                        core_cmd <= #1 `I2C_CMD_WRITE;                                    end                                else // 缺省的是 stop 狀態(tài)                                    begin                                        c_state <= #1 ST_STOP;                                        core_cmd <= #1 `I2C_CMD_STOP;                                        // 產(chǎn)生應(yīng)答信號(hào)                                        cmd_ack <= #1 1'b1;                                    end                                ld <= #1 1'b1;                            end                                                    //開始狀態(tài)                        ST_START:                            if (core_ack)                                begin                                    if (read)                                        begin                                            c_state <= #1 ST_READ;                                            core_cmd <= #1 `I2C_CMD_READ;                                        end                                    else                                        begin                                            c_state <= #1 ST_WRITE;                                            core_cmd <= #1 `I2C_CMD_WRITE;                                        end                                    ld <= #1 1'b1;                                end                                                        //寫數(shù)據(jù)狀態(tài)                        ST_WRITE:                            if (core_ack)                                if (cnt_done)                                    begin                                        c_state <= #1 ST_ACK;                                        core_cmd <= #1 `I2C_CMD_READ;                                    end                                else                                    begin                                        c_state <= #1 ST_WRITE; // 保持在原來狀態(tài)                                        core_cmd <= #1 `I2C_CMD_WRITE; // 寫下一位數(shù)據(jù)                                        shift <= #1 1'b1;                                    end                                                            //讀信號(hào)狀態(tài)                        ST_READ:                            if (core_ack)                                begin                                    if (cnt_done)                                        begin                                            c_state <= #1 ST_ACK;                                            core_cmd <= #1 `I2C_CMD_WRITE;                                        end                                    else                                        begin                                            c_state <= #1 ST_READ; // 保留在原來狀態(tài)                                            core_cmd <= #1 `I2C_CMD_READ; // 讀下一位數(shù)據(jù)                                        end                                    shift <= #1 1'b1;                                    core_txd <= #1 ack_in;                                end                                                        //應(yīng)答數(shù)據(jù)狀態(tài)                        ST_ACK:                            if (core_ack)                                begin                                    if (stop)                                        begin                                            c_state <= #1 ST_STOP;                                            core_cmd <= #1 `I2C_CMD_STOP;                                        end                                    else                                        begin                                            c_state <= #1 ST_IDLE;                                            core_cmd <= #1 `I2C_CMD_NOP;                                        end                                                                                // 把應(yīng)答信號(hào)輸出連接到位控制模塊                                        ack_out <= #1 core_rxd;                                        // 產(chǎn)生應(yīng)答信號(hào)                                        cmd_ack <= #1 1'b1;                                        core_txd <= #1 1'b1;                                end                            else                                core_txd <= #1 ack_in;                                                        //停止?fàn)顟B(tài)                        ST_STOP:                            if (core_ack)                                begin                                    c_state <= #1 ST_IDLE;                                    core_cmd <= #1 `I2C_CMD_NOP;                                end                    endcase                end                endmodule

3.3 程序主體的實(shí)現(xiàn)

程序主體部分完成與外部程序的接口、與總線上外部節(jié)點(diǎn)的連線、完成程序內(nèi)部各個(gè)寄存器的構(gòu)建、控制字節(jié)傳輸控制模塊等功能。代碼如下:

`include "timescale.v"`include "i2c_master_defines.v"//模塊定義module i2c_master_top(    wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o,    wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o,    scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o );        // 參數(shù)    parameter ARST_LVL = 1'b0; // 異步 reset 信號(hào)        // 輸入、輸出信號(hào)    // 連接到外部接口的信號(hào)    input clk_i; // 主節(jié)點(diǎn)時(shí)鐘信號(hào)    input rst_i; // 同步 reset 信號(hào),高有效    input arst_i; // 異步 reset 信號(hào)    input [2:0] adr_i; // 低位地址信號(hào)    input [7:0] dat_i; // 數(shù)據(jù)總線輸入    output [7:0] dat_o; // 數(shù)據(jù)總線輸出    input we_i; // 輸入使能信號(hào)    input stb_i; // 觸發(fā)信號(hào)    input cyc_i; // 總線周期輸入    output ack_o; // 應(yīng)答信號(hào)輸出    output inta_o; // 中斷請(qǐng)求信號(hào)輸出    reg [7:0] wb_dat_o;    reg wb_ack_o;    reg wb_inta_o;????????//?I2C?信號(hào)????//?I2C?時(shí)鐘信號(hào)線    input scl_pad_i; // SCL 輸入    output scl_pad_o; // SCL 輸出    output scl_padoen_o; // SCL 輸出使能????????//?I2C?數(shù)據(jù)線    input sda_pad_i; // SDA 輸入    output sda_pad_o; // SDA 輸出    output sda_padoen_o; // SDA 輸出使能        // 變量申明    // 寄存器    reg [15:0] prer; // 時(shí)鐘分頻寄存器    reg [ 7:0] ctr; // 控制寄存器    reg [ 7:0] txr; // 數(shù)據(jù)傳輸寄存器    wire [ 7:0] rxr; // 數(shù)據(jù)接收寄存器    reg [ 7:0] cr; // 命令寄存器    wire [ 7:0] sr; // 狀態(tài)寄存器        // 完成信號(hào),命令完成后清除命令寄存器    wire done;        // 模塊使能信號(hào)    wire core_en;    wire ien;        // 狀態(tài)寄存器信號(hào)    wire irxack;    reg rxack; // 從從節(jié)點(diǎn)接收應(yīng)答信號(hào)    reg tip; // 傳輸進(jìn)行標(biāo)志    reg irq_flag; // 中斷掛起標(biāo)志    wire i2c_busy; // 總線忙標(biāo)志    wire i2c_al; // 總線仲裁丟失    reg al; // 狀態(tài)寄存器仲裁丟失位        // 模塊主體    // 產(chǎn)生內(nèi)部 reset    wire rst_i = arst_i ^ ARST_LVL;    wire wacc = cyc_i & stb_i & we_i;        // 產(chǎn)生應(yīng)答輸出信號(hào)    always @(posedge clk_i)        wb_ack_o <= #1 cyc_i & stb_i & ~ack_o;        // 數(shù)據(jù)輸出    always @(posedge clk_i)        begin            case (adr_i)                3'b000: wb_dat_o = prer[ 7:0];                3'b001: wb_dat_o = prer[15:8];                3'b010: wb_dat_o = ctr;                3'b011: wb_dat_o = rxr; // 寫數(shù)據(jù)傳輸寄存器                3'b100: wb_dat_o = sr; // 寫命令寄存器                3'b101: wb_dat_o = txr;                3'b110: wb_dat_o = cr;                3'b111: wb_dat_o = 0; // 保留位            endcase        end            // 產(chǎn)生寄存器    always @(posedge wb_clk_i or negedge rst_i)        if (!rst_i)            begin                prer <= #1 16'hffff;                ctr <= #1 8'h0;                txr <= #1 8'h0;            end        else if (wb_rst_i)            begin                prer <= #1 16'hffff;                ctr <= #1 8'h0;                txr <= #1 8'h0;            end        else            if (wb_wacc)                case (wb_adr_i) // synopsis full_case parallel_case                    3'b000 : prer [ 7:0] <= #1 wb_dat_i;                    3'b001 : prer [15:8] <= #1 wb_dat_i;                    3'b010 : ctr <= #1 wb_dat_i;                    3'b011 : txr <= #1 wb_dat_i;                endcase        // 產(chǎn)生命令寄存器    always @(posedge wb_clk_i or negedge rst_i)        if (~rst_i)            cr <= #1 8'h0;        else if (wb_rst_i)            cr <= #1 8'h0;        else if (wb_wacc)            begin                if (core_en & (wb_adr_i == 3'b100) )                    cr <= #1 wb_dat_i;            end         else            begin                if (done | i2c_al)    ????????????????cr[7:4]?<=?#1?4'h0;?//?命令完成或者仲裁丟失時(shí)清除命令寄存器內(nèi)容                    cr[2:1] <= #1 2'b0; // 保留位                    cr[0] <= #1 2'b0; // 清除 IRQ_ACK 位            end                // 譯碼命令寄存器    wire sta = cr[7];    wire sto = cr[6];    wire rd = cr[5];    wire wr = cr[4];    wire ack = cr[3];    wire iack = cr[0];        // 譯碼控制寄存器    assign core_en = ctr[7];    assign ien = ctr[6];        // 連接字節(jié)控制模塊    i2c_master_byte_ctrl byte_controller (            .clk ( wb_clk_i ),            .rst ( wb_rst_i ),            .nReset ( rst_i ),            .ena ( core_en ),            .clk_cnt ( prer ),            .start ( sta ),            .stop ( sto ),            .read ( rd ),            .write ( wr ),            .ack_in ( ack ),            .din ( txr ),            .cmd_ack ( done ),            .ack_out ( irxack ),            .dout ( rxr ),            .i2c_busy ( i2c_busy ),            .i2c_al ( i2c_al ),            .scl_i ( scl_pad_i ),            .scl_o ( scl_pad_o ),            .scl_oen ( scl_padoen_o ),            .sda_i ( sda_pad_i ),            .sda_o ( sda_pad_o ),            .sda_oen ( sda_padoen_o )        )            // 狀態(tài)寄存器部分和中斷請(qǐng)求信號(hào)    always @(posedge wb_clk_i or negedge rst_i)        if (!rst_i)            begin                al <= #1 1'b0;                rxack <= #1 1'b0;                tip <= #1 1'b0;                irq_flag <= #1 1'b0;            end        else if (wb_rst_i)            begin                al <= #1 1'b0;                rxack <= #1 1'b0;                tip <= #1 1'b0;                irq_flag <= #1 1'b0;            end        else            begin                al <= #1 i2c_al | (al & ~sta);                rxack <= #1 irxack;                tip <= #1 (rd | wr);????????????????irq_flag?<=?#1?(done?|?i2c_al?|?irq_flag)?&?~iack;???            end                // 中斷請(qǐng)求標(biāo)志    // 產(chǎn)生中斷請(qǐng)求信號(hào)    always @(posedge wb_clk_i or negedge rst_i)        if (!rst_i)            wb_inta_o <= #1 1'b0;        else if (wb_rst_i)            wb_inta_o <= #1 1'b0;        else            wb_inta_o <= #1 irq_flag && ien; //中斷使能位 IEN 設(shè)置后產(chǎn)生中斷信號(hào)        assign sr[7] = rxack;    assign sr[6] = i2c_busy;    assign sr[5] = al;    assign sr[4:2] = 3'h0; // reserved    assign sr[1] = tip;    assign sr[0] = irq_flag;    endmodule

四、程序的仿真與測(cè)試

I2C 協(xié)議的模擬程序完成后,還需要通過仿真程序?qū)Τ绦虻墓δ苓M(jìn)行測(cè)試。對(duì)本程序的仿真包括 3 個(gè)部分:第一部分是主節(jié)點(diǎn)的仿真,模擬數(shù)據(jù)讀/寫;第二部分是從節(jié)點(diǎn)的仿真,模擬數(shù)據(jù)的接收和應(yīng)答;第三部分是仿真主程序,負(fù)責(zé)整個(gè)仿真過程的控制。

4.1 主節(jié)點(diǎn)的仿真

主節(jié)點(diǎn)仿真的內(nèi)容包括讀數(shù)據(jù)、寫數(shù)據(jù)和比較數(shù)據(jù) 3 部分,代碼如下:

`include "timescale.v"//模塊定義module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, sel, ack, err, rty);    //參數(shù)    parameter dwidth = 32;    parameter awidth = 32;        //輸入、輸出    input clk, rst;    output [awidth -1:0] adr;    input [dwidth -1:0] din;    output [dwidth -1:0] dout;    output cyc, stb;    output we;    output [dwidth/8 -1:0] sel;    input ack, err, rty;        //WIRE 定義    reg [awidth -1:0] adr;    reg [dwidth -1:0] dout;    reg cyc, stb;    reg we;    reg [dwidth/8 -1:0] sel;    reg [dwidth -1:0] q;        // 存儲(chǔ)邏輯    //初始化    initial        begin            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            cyc = 1'b0;            stb = 1'bx;            we = 1'hx;            sel = {dwidth/8{1'bx}};            #1;        end            // 寫數(shù)據(jù)周期    task wb_write;        input delay;        integer delay;        input [awidth -1:0] a;        input [dwidth -1:0] d;        begin            // 延遲            repeat(delay) @(posedge clk);            // 設(shè)置信號(hào)值            #1;            adr = a;            dout = d;            cyc = 1'b1;            stb = 1'b1;            we = 1'b1;            sel = {dwidth/8{1'b1}};            @(posedge clk);            // 等待從節(jié)點(diǎn)的應(yīng)答信號(hào)            while(~ack) @(posedge clk);            #1;            cyc = 1'b0;            stb = 1'bx;            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            we = 1'hx;            sel = {dwidth/8{1'bx}};        end    endtask        // 讀數(shù)據(jù)周期    task wb_read;        input delay;        integer delay;        input [awidth -1:0]a;        output [dwidth -1:0] d;        begin            // 延遲            repeat(delay) @(posedge clk);            // 設(shè)置信號(hào)值            #1;            adr = a;            dout = {dwidth{1'bx}};            cyc = 1'b1;            stb = 1'b1;            we = 1'b0;            sel = {dwidth/8{1'b1}};            @(posedge clk);            // 等待從節(jié)點(diǎn)應(yīng)答信號(hào)            while(~ack) @(posedge clk);            #1;            cyc = 1'b0;            stb = 1'bx;            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            we = 1'hx;            sel = {dwidth/8{1'bx}};            d = din;        end    endtask        // 比較數(shù)據(jù)    task wb_cmp;        input delay;        integer delay;        input [awidth -1:0] a;        input [dwidth -1:0] d_exp;        begin            wb_read (delay, a, q);            if (d_exp !== q)????????????$display("Data?compare?error.?Received?%h,?expected?%h?at?time?%t",?q,?d_exp,$time);        end    endtaskendmodule

4.2 從節(jié)點(diǎn)的仿真

從節(jié)點(diǎn)仿真程序需要模擬從主節(jié)點(diǎn)接收數(shù)據(jù),并發(fā)出應(yīng)答信號(hào),代碼如下:

`include "timescale.v"//模塊定義module i2c_slave_model (scl, sda);    // 參數(shù)    // 地址    parameter I2C_ADR = 7'b001_0000;        // 輸入、輸出    input scl;    inout sda;        // 變量申明    wire debug = 1'b1;    reg [7:0] mem [3:0]; // 初始化內(nèi)存    reg [7:0] mem_adr; // 內(nèi)存地址    reg [7:0] mem_do; // 內(nèi)存數(shù)據(jù)輸出    reg sta, d_sta;    reg sto, d_sto;    reg [7:0] sr; // 8 位移位寄存器    reg rw; // 讀寫方向    wire my_adr; // 地址    wire i2c_reset; // RESET 信號(hào)    reg [2:0] bit_cnt;    wire acc_done; // 傳輸完成    reg ld;    reg sda_o;    wire sda_dly;        // 狀態(tài)機(jī)的狀態(tài)定義    parameter idle = 3'b000;    parameter slave_ack = 3'b001;    parameter get_mem_adr = 3'b010;    parameter gma_ack = 3'b011;    parameter data = 3'b100;    parameter data_ack = 3'b101;    reg [2:0] state;        // 模塊主體    //初始化    initial        begin            sda_o = 1'b1;            state = idle;        end            // 產(chǎn)生移位寄存器    always @(posedge scl)        sr <= #1 {sr[6:0],sda};            //檢測(cè)到訪問地址與從節(jié)點(diǎn)一致    assign my_adr = (sr[7:1] == I2C_ADR);        //產(chǎn)生位寄存器    always @(posedge scl)        if(ld)            bit_cnt <= #1 3'b111;        else            bit_cnt <= #1 bit_cnt - 3'h1;                //產(chǎn)生訪問結(jié)束標(biāo)志    assign acc_done = !(|bit_cnt);        // sda 延遲    assign #1 sda_dly = sda;        //檢測(cè)到開始狀態(tài)    always @(negedge sda)        if(scl)            begin                sta <= #1 1'b1;                    if(debug)                        $display("DEBUG i2c_slave; start condition detected at %t", $time);            end        else            sta <= #1 1'b0;                always @(posedge scl)        d_sta <= #1 sta;        // 檢測(cè)到停止?fàn)顟B(tài)信號(hào)    always @(posedge sda)        if(scl)            begin                sto <= #1 1'b1;                    if(debug)                        $display("DEBUG i2c_slave; stop condition detected at %t", $time);            end        else            sto <= #1 1'b0;                //產(chǎn)生 I2C 的 RESET 信號(hào)    assign i2c_reset = sta || sto;        // 狀態(tài)機(jī)    always @(negedge scl or posedge sto)        if (sto || (sta && !d_sta) )            begin                state <= #1 idle; // reset 狀態(tài)機(jī)                sda_o <= #1 1'b1;                ld <= #1 1'b1;            end        else            begin            // 初始化            sda_o <= #1 1'b1;            ld <= #1 1'b0;            case(state)                idle: // idle 狀態(tài)                    if (acc_done && my_adr)                        begin                            state <= #1 slave_ack;                            rw <= #1 sr[0];                            sda_o <= #1 1'b0; // 產(chǎn)生應(yīng)答信號(hào)                            #2;                            if(debug && rw)                                $display("DEBUG?i2c_slave;?command?byte?received?(read)?at?%t",$time);                            if(debug && !rw)                                $display("DEBUG?i2c_slave;?command?byte?received?(write)?at?%t",$time);                            if(rw)                                begin                                    mem_do <= #1 mem[mem_adr];                                        if(debug)                                            begin                                                #2?$display("DEBUG?i2c_slave;?data?block?read?%x?from address?%x?(1)",?mem_do,?mem_adr);                                                #2?$display("DEBUG?i2c_slave;?memcheck?[0]=%x,?[1]=%x, [2]=%x",?mem[4'h0],?mem[4'h1],?mem[4'h2]);                                            end                                end                        end                    slave_ack:                        begin                            if(rw)                                begin                                    state <= #1 data;                                    sda_o <= #1 mem_do[7];                                end                            else                                state <= #1 get_mem_adr;                                ld <= #1 1'b1;?????????????????????????end?????????????????????????????                    get_mem_adr: // 等待內(nèi)存地址                        if(acc_done)                            begin                                state <= #1 gma_ack;                                mem_adr <= #1 sr; // 保存內(nèi)存地址                                sda_o <= #1 !(sr <= 15); // 收到合法地址信號(hào)后發(fā)出應(yīng)答信號(hào)                            if(debug)                                #1?$display("DEBUG?i2c_slave;?address?received.?adr=%x,?ack=%b",sr,?sda_o);                            end                                                gma_ack:                        begin                            state <= #1 data;                            ld <= #1 1'b1;                        end                                       data: // 接收數(shù)據(jù)                        begin                            if(rw)                                sda_o <= #1 mem_do[7];                            if(acc_done)                                begin                                    state <= #1 data_ack;                                    mem_adr <= #2 mem_adr + 8'h1;                                    sda_o <= #1 (rw && (mem_adr <= 15) );                                if(rw)                                    begin                                        #3 mem_do <= mem[mem_adr];                                        if(debug)                                            #5?$display("DEBUG?i2c_slave;?data?block?read?%x?from address?%x?(2)",?mem_do,?mem_adr);                                    ????????????????????????????????????end                                if(!rw)                                    begin                                        mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory                                        if(debug)                                            #2?$display("DEBUG?i2c_slave;?data?block?write?%x?to address?%x",?sr,?mem_adr);                                    end                                end                            end                                                    data_ack:                            begin                                ld <= #1 1'b1;                                if(rw)                                    if(sda) //                                        begin                                            state <= #1 idle;                                            sda_o <= #1 1'b1;                                        end                                    else                                        begin                                            state <= #1 data;                                            sda_o <= #1 mem_do[7];                                        end                                    else                                        begin                                            state <= #1 data;                                            sda_o <= #1 1'b1;                                        end                                end                            endcase                        end                            // 從內(nèi)存讀數(shù)據(jù)    always @(posedge scl)    if(!acc_done && rw)    mem_do <= #1 {mem_do[6:0], 1'b1};        // 產(chǎn)生三態(tài)    assign sda = sda_o ? 1'bz : 1'b0;        // 檢查時(shí)序    wire tst_sto = sto;    wire tst_sta = sta;    wire tst_scl = scl;        //指定各個(gè)信號(hào)的上升沿和下降沿    specify        specparam normal_scl_low = 4700,            normal_scl_high = 4000,            normal_tsu_sta = 4700,            normal_tsu_sto = 4000,            normal_sta_sto = 4700,            fast_scl_low = 1300,            fast_scl_high = 600,            fast_tsu_sta = 1300,            fast_tsu_sto = 600,            fast_sta_sto = 1300;        $width(negedge scl, normal_scl_low);        $width(posedge scl, normal_scl_high);        $setup(negedge sda &&& scl, negedge scl, normal_tsu_sta); // 開始狀態(tài)信號(hào)        $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // 停止?fàn)顟B(tài)信號(hào)        $setup(posedge tst_sta, posedge tst_scl, normal_sta_sto);    endspecify    endmodule

4.3 仿真主程序

仿真主程序完成主節(jié)點(diǎn)數(shù)據(jù)到從節(jié)點(diǎn)的控制,代碼如下:

`include "timescale.v"//模塊定義module?tst_bench_top();    //連線和寄存器    reg clk;    reg rstn;    wire [31:0] adr;    wire [ 7:0] dat_i, dat_o;    wire we;    wire stb;    wire cyc;    wire ack;    wire inta;        //q 保存狀態(tài)寄存器內(nèi)容    reg [7:0] q, qq;    wire scl, scl_o, scl_oen;    wire sda, sda_o, sda_oen;        //寄存器地址    parameter PRER_LO = 3'b000; //分頻寄存器低位地址    parameter PRER_HI = 3'b001; //高位地址    parameter?CTR?=?3'b010;?//控制寄存器地址,(7)使能位|6?中斷使能位|5-0其余保留位    parameter?RXR?=?3'b011;?//接收寄存器地址,(7)接收到的最后一個(gè)字節(jié)的數(shù)據(jù)    parameter TXR = 3'b011; //傳輸寄存器地址,(7)傳輸?shù)刂窌r(shí)最后一位為讀寫位,1 為讀    parameter CR = 3'b100; //命令寄存器地址,???????//(7)開始|6?結(jié)束|5?讀|4?寫|3?應(yīng)答(作為接收方時(shí),發(fā)送應(yīng)答信號(hào),“0”為應(yīng)答,“1”為不應(yīng)答)|2?保留位|1?保留位|0?中斷應(yīng)答位,這八位自動(dòng)清除????parameter?SR?=?3'b100;?//狀態(tài)寄存器地址,(7)接收應(yīng)答位(“0”為接收到應(yīng)答)|6?忙位(產(chǎn)生開始信號(hào)后變?yōu)?1,結(jié)束信號(hào)后變?yōu)?0)|5?仲裁位|4-2?保留位|1?傳輸中位(1?表示正在傳輸數(shù)據(jù),0?表示傳輸結(jié)束)|中斷標(biāo)志位    parameter TXR_R = 3'b101;    parameter CR_R = 3'b110;        // 產(chǎn)生時(shí)鐘信號(hào),一個(gè)時(shí)間單位為 1ns,周期為 10ns,頻率為 100MHz。    always #5 clk = ~clk;        //連接 master 模擬模塊    wb_master_model #(8, 32) u0 (            .clk(clk), //時(shí)鐘            .rst(rstn), //重起            .adr(adr), //地址            .din(dat_i), //輸入的數(shù)據(jù)            .dout(dat_o), //輸出的數(shù)據(jù)            .cyc(cyc),            .stb(stb),            .we(we),            .sel(),            .ack(ack), //應(yīng)答            .err(1'b0),            .rty(1'b0)        );        //連接 i2c 接口    i2c_master_top i2c_top (            //連接到 master 模擬模塊部分            .wb_clk_i(clk), //時(shí)鐘            .wb_rst_i(1'b0), //同步重起位            .arst_i(rstn), //異步重起            .wb_adr_i(adr[2:0]), //地址輸入            .wb_dat_i(dat_o), //數(shù)據(jù)輸入接口            .wb_dat_o(dat_i), //數(shù)據(jù)從接口輸出            .wb_we_i(we), //寫使能信號(hào)            .wb_stb_i(stb), //片選信號(hào),應(yīng)該一直為高            .wb_cyc_i(cyc),            .wb_ack_o(ack), //應(yīng)答信號(hào)輸出到 master 模擬模塊            .wb_inta_o(inta), //中斷信號(hào)輸出到 master 模擬模塊                        //輸出的 i2c 信號(hào),連接到 slave 模擬模塊            .scl_pad_i(scl),            .scl_pad_o(scl_o),            .scl_padoen_o(scl_oen),            .sda_pad_i(sda),            .sda_pad_o(sda_o),            .sda_padoen_o(sda_oen)        );        //連接到 slave 模擬模塊    i2c_slave_model #(7'b1010_000) i2c_slave (            .scl(scl),            .sda(sda)        );            //為 master 模擬模塊產(chǎn)生 scl 和 sda 的三態(tài)緩沖    assign scl = scl_oen ? 1'bz : scl_o; // create tri-state buffer for i2c_master scl line    assign sda = sda_oen ? 1'bz : sda_o; // create tri-state buffer for i2c_master sda line        //上拉    pullup p1(scl); // pullup scl line    pullup p2(sda); // pullup sda line        //初始化    initial        begin            $display("n 狀態(tài): %t I2C 接口測(cè)試開始!nn", $time);            // 初始值            clk = 0;            //重起系統(tǒng)            rstn = 1'b1; // negate reset            #2;            rstn = 1'b0; // assert reset            repeat(20) @(posedge clk);            rstn = 1'b1; // negate reset            $display("狀態(tài): %t 完成系統(tǒng)重起!", $time);            @(posedge clk);            // 對(duì)接口編程            // 寫內(nèi)部寄存器            // 分頻 100M/100K*5=O'200=h'C8            u0.wb_write(1, PRER_LO, 8'hc7);            u0.wb_write(1, PRER_HI, 8'h00);            $display("狀態(tài): %t 完成分頻寄存器操作!", $time);            //讀分頻寄存器內(nèi)容            u0.wb_cmp(0, PRER_LO, 8'hc8);            u0.wb_cmp(0, PRER_HI, 8'h00);            $display("狀態(tài): %t 完成分頻寄存器確認(rèn)操作!", $time);            //接口使能            u0.wb_write(1, CTR, 8'h80);            $display("狀態(tài): %t 完成接口使能!", $time);            // 驅(qū)動(dòng) slave 地址            // h'a0=b'1010_0000,地址+寫狀態(tài),寫入的地址為 h'50            u0.wb_write(1, TXR, 8'ha0);            //命令內(nèi)容為 b'1001_0000,產(chǎn)生開始位,并設(shè)置寫狀態(tài)            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生開始位, 然后寫命令 a0(地址+寫),命令開始!", $time);            // 檢查狀態(tài)位信息            // 檢查傳輸是否結(jié)束            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(0, SR, q);            $display("狀態(tài): %t 地址驅(qū)動(dòng)寫操作完成!", $time);            // 待寫的地址為 h'01            u0.wb_write(1, TXR, 8'h01);            // 產(chǎn)生寫命令 b'0001_0000            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 待寫地址為 01,命令開始!", $time);            // 檢查狀態(tài)位            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(0, SR, q);            $display("狀態(tài): %t 寫操作完成!", $time);            // 寫入內(nèi)容            u0.wb_write(1, TXR, 8'ha5);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 寫入內(nèi)容為 a5,開始寫入過程!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 寫 a5 到地址 h'01 中完成!", $time);            // 寫入下一個(gè)地址 5a            u0.wb_write(1, TXR, 8'h5a); // present data            // 寫入并停止            u0.wb_write(0, CR, 8'h50); // set command (stop, write)            $display("狀態(tài): %t 寫 5a 到下一個(gè)地址,產(chǎn)生停止位!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 寫第二個(gè)地址結(jié)束!", $time);            // 讀            // 驅(qū)動(dòng) slave 地址            u0.wb_write(1, TXR, 8'ha0);            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生開始位,寫命令 a0 (slave 地址+write)", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t slave 地址驅(qū)動(dòng)完成!", $time);            // 發(fā)送地址            u0.wb_write(1, TXR, 8'h01);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 發(fā)送地址 01!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 地址發(fā)送完成!", $time);            // 驅(qū)動(dòng) slave 地址,1010_0001,h'50+read            u0.wb_write(1, TXR, 8'ha1);            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生重復(fù)開始位, 讀地址+開始位", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 命令結(jié)束!", $time);            // 讀數(shù)據(jù)            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀+應(yīng)答命令", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 讀結(jié)束!", $time);            // 檢查讀的內(nèi)容            u0.wb_read(1, RXR, qq);            if(qq !== 8'ha5)                $display("n 錯(cuò)誤: 需要的是 a5, received %x at time %t", qq, $time);            // 讀下一個(gè)地址內(nèi)容            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀+ 應(yīng)答", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第二個(gè)地址讀結(jié)束!", $time);            u0.wb_read(1, RXR, qq);            if(qq !== 8'h5a)            $display("n 錯(cuò)誤: 需要的是 5a, received %x at time %t", qq, $time);            // 讀            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀 + 應(yīng)答", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第三個(gè)地址讀完成!", $time);            u0.wb_read(1, RXR, qq);            $display("狀態(tài): %t 第三個(gè)地址內(nèi)容是 %x !", $time, qq);            // 讀            u0.wb_write(1, CR, 8'h28);            $display("狀態(tài): %t 讀 + 不應(yīng)答!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第四個(gè)地址讀完成!", $time);            u0.wb_read(1, RXR, qq);            $display("狀態(tài): %t 第四個(gè)地址內(nèi)容為 %x !", $time, qq);            // 檢查不存在的 slave 地址            // drive slave address            u0.wb_write(1, TXR, 8'ha0);            u0.wb_write(0, CR, 8'h90);????????????$display("狀態(tài):?%t 產(chǎn)生開始位, 發(fā)送命令 a0?(slave 地址+寫). 檢查非法地址!",$time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 命令結(jié)束!", $time);            // 發(fā)送內(nèi)存地址            u0.wb_write(1, TXR, 8'h10);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 發(fā)送 slave 內(nèi)存地址 10!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 地址發(fā)送完畢!", $time);            // slave 發(fā)送不應(yīng)答            $display("狀態(tài): %t 檢查不應(yīng)答位!", $time);            if(!q[7])            $display("n 錯(cuò)誤: 需要 NACK, 接收到 ACKn");            // 從 slave 讀數(shù)據(jù)            u0.wb_write(1, CR, 8'h40);            $display("狀態(tài): %t 產(chǎn)生'stop'位", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 結(jié)束!", $time);            #25000; // wait 25us            $display("nn 狀態(tài): %t 測(cè)試結(jié)束!", $time);            $finish;        endendmodule

4.4 仿真結(jié)果

在 ModelSim 中可以看到仿真的結(jié)果。如圖 7 所示是發(fā)送開始狀態(tài)并寫地址“a0”時(shí)的圖形,此時(shí)在圖上表示為 SCL 處于高時(shí) SDA 的一個(gè)下降沿,然后是數(shù)據(jù)“1010,0000”。

圖 7 發(fā)送開始信號(hào)并寫地址 a0

如圖 8 所示為發(fā)送數(shù)據(jù)“01”和“a5”時(shí)的圖形,在圖上表示為:數(shù)據(jù)“0000,0001”和“1010,0101”。

圖 8 發(fā)送數(shù)據(jù)“01”和“a5”

如圖 9 所示的是發(fā)送停止?fàn)顟B(tài)信號(hào)和數(shù)據(jù)“5a”時(shí)的圖形,在圖上表示為 SCL 處于高時(shí)SDA 的一個(gè)上升沿,然后是數(shù)據(jù)“0101,1010”。

圖 9 發(fā)送停止?fàn)顟B(tài)信號(hào)和數(shù)據(jù)“5a”

仿真程序說明?I2C?程序符合?I2C?協(xié)議的時(shí)序和數(shù)據(jù)格式,可以實(shí)現(xiàn)模擬?I2C?協(xié)議的任務(wù)。

五、總結(jié)

本篇首先說明了?I2C?協(xié)議相關(guān)的內(nèi)容,介紹協(xié)議基本概念和數(shù)據(jù)傳輸各個(gè)命令的具體含義以及協(xié)議對(duì)時(shí)序的要求。接下來介紹模擬?I2C?協(xié)議程序的框架,詳細(xì)講解框架中各個(gè)模塊的功能并介紹詳細(xì)代碼。最后通過一個(gè)完成的仿真程序完成對(duì)程序的測(cè)試。I2C?在應(yīng)用中有著廣泛的用途,本篇希望通過這個(gè)例子為各位大俠提供一個(gè)可行的解決方案。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
LFE3-150EA-6FN672I 1 Lattice Semiconductor Corporation Field Programmable Gate Array, 375MHz, 149000-Cell, PBGA672, 27 X 27 MM, LEAD FREE, FPBGA-672

ECAD模型

下載ECAD模型
$243.42 查看
EP3C5F256C8N 1 Altera Corporation Field Programmable Gate Array, 5136 CLBs, 472.5MHz, 5136-Cell, CMOS, PBGA256, 17 X 17 MM, 1.55 MM HEIGHT, 1 MM PITCH, LEAD FREE, FBGA-256

ECAD模型

下載ECAD模型
$23.27 查看
EP2C8Q208I8N 1 Altera Corporation Field Programmable Gate Array, 516 CLBs, 402.5MHz, 8256-Cell, CMOS, PQFP208, LEAD FREE, PLASTIC, QFP-208
$39.64 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

任何技術(shù)的學(xué)習(xí)就好比一個(gè)江湖,對(duì)于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。