国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

FPGA基礎學習IIC協議驅動設計詳解

電子工程師 ? 來源:CSDN技術社區 ? 作者: FPGA技術江湖 ? 2021-05-02 09:15 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本系列將帶來FPGA的系統性學習,從最基本的數字電路基礎開始,最詳細操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、信息、通信類專業學生、初入職場小白及打算進階提升的職業開發者都可以有系統性學習的機會。

系統性的掌握技術開發以及相關要求,對個人就業以及職業發展都有著潛在的幫助,希望對大家有所幫助。后續會陸續更新 Xilinx 的 Vivado、ISE 及相關操作軟件的開發的相關內容,學習FPGA設計方法及設計思想的同時,實操結合各類操作軟件,會讓你在技術學習道路上無比的順暢,告別技術學習小BUG卡破腦殼,告別目前忽悠性的培訓誘導,真正的去學習去實戰應用,這種快樂試試你就會懂的。話不多說,上貨。

本篇實現基于叁芯智能科技的SANXIN -B01 FPGA開發板,以下為配套的教程,如有入手開發板,可以登錄官方淘寶店購買,還有配套的學習視頻。

I2C 即 Inter-Integrated Circuit(集成電路總線),這種總線類型是由飛利浦半導體公司在八十年代初設計出來的一種簡單、雙向、二線制、同步串行總線,主要是用來連接整體電路(ICS) ,I2C是一種多向控制總線,也就是說多個芯片可以連接到同一總線結構下,同時每個芯片都可以作為實時數據傳輸的控制源。這種方式簡化了信號傳輸總線接口。

I2C 總線是一種串行數據總線,只有二根信號線,一根是雙向的數據線 SDA,另一根是時鐘線 SCL,兩條線可以掛多個設備。 I2C 設備(絕大多數)里有個固化的地址,只有在兩條線上傳輸的值等于 I2C 設備的固化地址時,其才會作出響應。通常我們為了方便把 I2C 設備分為主設備和從設備,基本上誰控制時鐘線(即控制SCL的電平高低變換)誰就是主設備。

64639d50-a08a-11eb-8b86-12bb97331649.png

A段為空閑段,在不進行數據傳輸時,SCL 和 SDA 保持為高電平。

B段為起始段(START CONDITION),在進行任何數據傳輸前,都應該先有一個起始段。當 SCL 為高電平期間,SDA有一個從高到低的變化視為起始段。

C段為結束段(STOP CONDITION),在數據傳輸結束后,都應該有一個結束段。當SCL為高電平期間,SDA有一個從低到高的變化視為結束段。

D段分為兩段數據有效段(ADDRESS OR ACKNOWLEDGE)和數據修改段(DATA ALLOWED TO CHANGE)。在傳輸數據時,在 SCL 為高電平期間,SDA 不能夠發生改變,視為數據有效段;在傳輸數據時,在 SCL 為低電平期間,SDA 可以發生改變(修改為下一個要發送的bit),視為數據修改段。

IIC的傳輸總結為:空閑時,SCL 和 SDA 都是高電平;發送信息前,首先發送起始段(SCL為高時,SDA有一個從高到低的變化);在發送信息時,每一個 SCL 脈沖,發送或者接收1bit,該bit在SCL為低電平時發生改變,在SCL為高電平時,保持穩定;發送信息完成后,發送結束段(SCL 為高時,SDA 有一個從低到高的變化)。

SDA的數據線是雙向傳輸的。在讀取時,SDA 從從設備到主設備;在寫入時,SDA從主設備到從設備。

在 IIC 傳輸時,為了保證通信的成功。發送方每發送8 bit 數據后,要求接收方回復一個應答位。

648b26f4-a08a-11eb-8b86-12bb97331649.png

應答位為低電平有效。

EEPROM(24LC64)簡介

24LC64是容量為8K*8bit(64Kbit)通信接口為IIC的EEPROM。EEPROM擁有掉電不丟失的功能,經常用來保存一些配置信息等等。這個器件可以一次進行最多32字節的頁寫入和一次進行整片讀出的功能。

64cce12a-a08a-11eb-8b86-12bb97331649.png

64db8568-a08a-11eb-8b86-12bb97331649.png

A0、A1、A2為用戶可配置的芯片選擇端。該管腳一般都連接電源或者地,在通信時,24LC64作為從機,主機在發送的命令中存在地址號,當地址號與A0、A1、A2管腳的電平相同時,該芯片視為被選中。

SDA和SCL為IIC協議中的時鐘線和數據線。

WP為寫保護端,當連接高電平時,該芯片只能夠被讀,不能夠寫入;當連接低電平或者空置時,該芯片可以被讀寫。

24LC64的寫入方式有兩種:單字節寫入和多字節寫入。

單字節寫入時序圖:

64fc6486-a08a-11eb-8b86-12bb97331649.png

在控制字節中,1010為EEPROM的標號;A2、A1、A0為主機發送的地址號,接收設備接收到此信息后,會與本身的A2、A1、A0相對比,相同時,視為被選中;最后1bit,0表示寫入、1表示讀出。

在高字節地址中,高三位地址無效。24LC64的地址范圍為8K,地址位13位即可。13位地址分為高五低八,在IIC中規定每次發送8bit,所以高五的前面必須發送任意3bit。

多字節寫入時序圖:

65328e30-a08a-11eb-8b86-12bb97331649.png

DATA BYTE 0被寫入到指定的地址中,后續的數據寫入到指定地址的后續地址中。EEPROM中每32個字節分為一塊,寫入時不能跨塊寫入。

24LC64的讀出方式有三種:當前地址單字節讀、任意地址單字節讀和任意地址多字節讀。

當前地址單字節讀的時序圖如下:

655281b8-a08a-11eb-8b86-12bb97331649.png

當主機接收24LC64的數據后,不發送ACK,而是發送高電平。

任意地址單字節讀取時序圖,如下:

65701232-a08a-11eb-8b86-12bb97331649.png

首先寫入指定地址,然后按照當前地址讀即可。

任意地址多字節讀,時序如下:

658d7d5e-a08a-11eb-8b86-12bb97331649.png

任意地址多字節讀就是在任意地址單字節讀的時序中,接收到從機發送的數據后,不要發送NO ACK而是發送ACK,此時從機就會繼續發送后續地址的數據,直到讀取的長度符合設計要求,然后發送NO ACK以及STOP結束傳輸。

下圖為24LC64的時序參數圖。

6595793c-a08a-11eb-8b86-12bb97331649.png

65b110e8-a08a-11eb-8b86-12bb97331649.png

65d84dde-a08a-11eb-8b86-12bb97331649.png

根據時序參數,決定將IIC的速率定為50KHz。發送時,數據改變在SCL的低電平的正中間;讀取時,在SCL高電平的正中間進行讀取。

IIC一般用在配置或者讀取少量的信息,對于速率來說要求相對較低。

硬件介紹

在開發板上,搭載了一個24LC64。

電路原理圖如下:

65effcf4-a08a-11eb-8b86-12bb97331649.png

A0、A1、A2都被接地,主機再發送控制字節時,需要將此三位發送低電平。

WP管腳被接地,24LC64的整個片子都可以被讀寫。

雙向IO介紹

在IIC協議中,SDA作為數據線使用,每一個SCL的脈沖,傳遞一bit的數據。在讀取時,SDA由24LC64發送,FPGA接收;在寫入時,SDA由FPGA發送,24LC64接收。所以IIC協議只能實現半雙工通信。

在硬件電路中,一般是通過三態門來進行控制雙向IO。

661e8ef2-a08a-11eb-8b86-12bb97331649.png

此電路在FPGA的IO中也是存在的。下面來分析具體的工作原理

三態門有三個端子,分別是輸入端(obuf),輸出端(SDA)和控制端(out_en)。工作方式為:當out_en有效時,obuf的值就可以輸出到SDA;當out_en無效時,obuf的值不能輸出到SDA,那么三態門就會輸出高阻態。

高阻態,字節理解為阻值特別大,也可以理解為斷開。高阻態與任何電平值相連接,所連接的線上的電平值都是和對方保持一致。在此可以延伸一下:所有的端口都是輸出,包括輸入端口在內,也會對外呈現出一種狀態,所以輸入端口對外呈現的狀態就是高阻態。對于雙向IO來說,輸出時,正常輸出即可;輸入時,就要呈現為高阻態。

設計要求

設計任意地址的單字節讀寫控制器。大多數是配置或者讀取少量的信息,對于多字節的讀寫,可以采用多次單字節的讀寫完成。

設計分析

24LC64的尋址范圍為8K,地址bit共計13bit,所以需要高5bit和低8bit。在有些兼容IIC協議接口的器件中,尋址范圍較少,8bit足夠表示,所以在設計時,設計出適合兩種尋址方式的驅動。

SCL的實現采用計數器控制,根據規定好的頻率,在合適的地方進行拉高或者拉低。由于SDA的變化都發生在SCL為高或者為低的中間,所以還需要產生對應的脈沖。

SDA根據協議和對應的脈沖進行發送對應的協議段。

架構設計和信號說明

本模塊命名為iic_drive。

662c78dc-a08a-11eb-8b86-12bb97331649.png

66427646-a08a-11eb-8b86-12bb97331649.png

暫不連接的端口,在后續測試時,會有其他模塊來控制或者讀取。

iic_drive設計實現

iic_scl采用計數器的方式實現。外部的時鐘為50MHz,iic_scl為100KHz,所以計數器需要記錄500個時鐘周期。

計數器由cnt_en控制。cnt_en有效時,cnt從0到500不斷循環;當cnt_en無效時,cnt保持等于0。

iic_scl在cnt等于0至249時,保持高電平;在250至499時,保持低電平。cnt等于125時,正好是iic_scl高電平的中間,用脈沖flag_high表示;cnt等于375時,正好是iic_scl低電平的中間,用脈沖flag_low表示。

其他信號采用狀態機作為輸出。out_en為三態門控制信號,sda_obuf為三態門輸出端,drive_cnt為發送或者接收的計數器,temp為發送或者接收的中間寄存器

狀態轉移圖如下:

666996e0-a08a-11eb-8b86-12bb97331649.png

設計代碼為:

module iic_drive (

input wire clk, input wire rst_n, input wire start_flag, input wire [6:0] slave_addr, input wire wren, input wire addr_sel, input wire [15:0] addr, input wire [7:0] wdata, output reg iic_scl, inout wire iic_sda, output reg iic_done, output reg [7:0] rdata); parameter FREQ_clk = 50_000_000; parameter FREQ_iic = 100_000; localparam T_SCL = FREQ_clk/FREQ_iic; localparam IDLE = 16‘b0000_0000_0000_0001; localparam START = 16’b0000_0000_0000_0010; localparam CTRL = 16‘b0000_0000_0000_0100; localparam CTRL_ACK = 16’b0000_0000_0000_1000; localparam HADDR = 16‘b0000_0000_0001_0000; localparam HADDR_ACK = 16’b0000_0000_0010_0000; localparam LADDR = 16‘b0000_0000_0100_0000; localparam LADDR_ACK = 16’b0000_0000_1000_0000; localparam WR = 16‘b0000_0001_0000_0000; localparam WR_ACK = 16’b0000_0010_0000_0000; localparam RD_START = 16‘b0000_0100_0000_0000; localparam RD_CTRL = 16’b0000_1000_0000_0000; localparam RD_CTRL_ACK = 16‘b0001_0000_0000_0000; localparam RD = 16’b0010_0000_0000_0000; localparam NO_ACK = 16‘b0100_0000_0000_0000; localparam STOP = 16’b1000_0000_0000_0000; reg [15:0] c_state; reg [15:0] n_state; reg [31:0] cnt; reg cnt_en; reg flag_high; reg flag_low; reg out_en; reg sda_obuf; reg [3:0] drive_cnt; reg [7:0] temp; assign iic_sda = (out_en == 1‘b1) ? sda_obuf : 1’bz; always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) cnt 《= 32’d0; else if (cnt 《 T_SCL - 1‘b1 && cnt_en == 1’b1) cnt 《= cnt + 1‘b1; else cnt 《= 32’d0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) iic_scl 《= 1’b1; else if (cnt 《 T_SCL/2) iic_scl 《= 1‘b1; else iic_scl 《= 1’b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) flag_high 《= 1’b0; else if (cnt == T_SCL/4 - 1‘b1) flag_high 《= 1’b1; else flag_high 《= 1‘b0; end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) flag_low 《= 1‘b0; else if (cnt == (T_SCL * 3)/4 - 1’b1) flag_low 《= 1‘b1; else flag_low 《= 1’b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) c_state 《= IDLE; else c_state 《= n_state; end always @ * begin case (c_state) IDLE : begin if (start_flag == 1’b1) n_state = START; else n_state = IDLE; end START : begin if (flag_high == 1‘b1) n_state = CTRL; else n_state = START; end CTRL : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) n_state = CTRL_ACK; else n_state = CTRL; end CTRL_ACK : begin if (flag_high == 1’b1) if (iic_sda == 1‘b0) if (addr_sel == 1’b1) n_state = HADDR; else n_state = LADDR; else n_state = START; else n_state = CTRL_ACK; end HADDR : begin if (flag_low == 1‘b1 && drive_cnt == 4’d8) n_state = HADDR_ACK; else n_state = HADDR; end HADDR_ACK : begin if (flag_high == 1‘b1) if (iic_sda == 1’b0) n_state = LADDR; else n_state = START; else n_state = HADDR_ACK; end LADDR : begin if (flag_low == 1‘b1 && drive_cnt == 4’d8) n_state = LADDR_ACK; else n_state = LADDR; end LADDR_ACK : begin if (flag_high == 1‘b1) if (iic_sda == 1’b0) if (wren == 1‘b1) n_state = WR; else n_state = RD_START; else n_state = START; else n_state = LADDR_ACK; end WR : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) n_state = WR_ACK; else n_state = WR; end WR_ACK : begin if (flag_high == 1’b1) if (iic_sda == 1‘b0) n_state = STOP; else n_state = START; else n_state = WR_ACK; end STOP : begin if (flag_high == 1’b1) n_state = IDLE; else n_state = STOP; end RD_START : begin if (flag_high == 1‘b1) n_state = RD_CTRL; else n_state = RD_START; end RD_CTRL : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) n_state = RD_CTRL_ACK; else n_state = RD_CTRL; end RD_CTRL_ACK : begin if (flag_high == 1’b1) if (iic_sda == 1‘b0) n_state = RD; else n_state = START; else n_state = RD_CTRL_ACK; end RD : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) n_state = NO_ACK; else n_state = RD; end NO_ACK : begin if (flag_high == 1’b1) n_state = STOP; else n_state = NO_ACK; end default : n_state = IDLE; endcase end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) cnt_en 《= 1’b0; else case (c_state) IDLE : cnt_en 《= 1‘b0; CTRL_ACK, HADDR_ACK, LADDR_ACK, WR_ACK, RD_CTRL_ACK : begin if (flag_high == 1’b1) if (iic_sda == 1‘b0) cnt_en 《= 1’b1; else cnt_en 《= 1‘b0; else cnt_en 《= cnt_en; end default : cnt_en 《= 1’b1; endcase end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) out_en 《= 1’b0; else case (c_state) IDLE : out_en 《= 1‘b0; START : out_en 《= 1’b1; CTRL : begin if (flag_low == 1‘b1 && drive_cnt == 4’d8) out_en 《= 1‘b0; else out_en 《= 1’b1; end CTRL_ACK : out_en 《= 1‘b0; HADDR : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) out_en 《= 1’b0; else if (flag_low == 1‘b1) out_en 《= 1’b1; else out_en 《= out_en; end HADDR_ACK : out_en 《= 1‘b0; LADDR : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) out_en 《= 1’b0; else if (flag_low == 1‘b1) out_en 《= 1’b1; else out_en 《= out_en; end LADDR_ACK : out_en 《= 1‘b0; WR : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) out_en 《= 1’b0; else if (flag_low == 1‘b1) out_en 《= 1’b1; else out_en 《= out_en; end WR_ACK : out_en 《= 1‘b0; STOP : begin if (flag_low == 1’b1) out_en 《= 1‘b1; else out_en 《= out_en; end RD_START : begin if (flag_low == 1’b1) out_en 《= 1‘b1; else out_en 《= out_en; end RD_CTRL : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) out_en 《= 1’b0; else out_en 《= 1‘b1; end RD_CTRL_ACK : out_en 《= 1’b0; RD : begin if (flag_low == 1‘b1 && drive_cnt == 4’d8) out_en 《= 1‘b1; else out_en 《= 1’b0; end NO_ACK : out_en 《= 1‘b1; default : out_en 《= 1’b0; endcase end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) sda_obuf 《= 1’b1; else case (c_state) IDLE : sda_obuf 《= 1‘b1; START : begin if (flag_high == 1’b1) sda_obuf 《= 1‘b0; else sda_obuf 《= 1’b1; end CTRL : begin if (flag_low == 1‘b1 && drive_cnt 《 4’d8) sda_obuf 《= temp[7]; else sda_obuf 《= sda_obuf; end CTRL_ACK : sda_obuf 《= 1‘b0; HADDR : begin if (flag_low == 1’b1 && drive_cnt 《 4‘d8) sda_obuf 《= temp[7]; else sda_obuf 《= sda_obuf; end HADDR_ACK : sda_obuf 《= 1’b0; LADDR : begin if (flag_low == 1‘b1 && drive_cnt 《 4’d8) sda_obuf 《= temp[7]; else sda_obuf 《= sda_obuf; end LADDR_ACK : sda_obuf 《= 1‘b0; WR : begin if (flag_low == 1’b1 && drive_cnt 《 4‘d8) sda_obuf 《= temp[7]; else sda_obuf 《= sda_obuf; end WR_ACK : sda_obuf 《= 1’b0; STOP : begin if (flag_low == 1‘b1) sda_obuf 《= 1’b0; else if (flag_high == 1‘b1) sda_obuf 《= 1’b1; else sda_obuf 《= sda_obuf; end RD_START : begin if (flag_low == 1‘b1) sda_obuf 《= 1’b1; else if (flag_high == 1‘b1) sda_obuf 《= 1’b0; else sda_obuf 《= sda_obuf; end RD_CTRL : begin if (flag_low == 1‘b1 && drive_cnt 《 4’d8) sda_obuf 《= temp[7]; else sda_obuf 《= sda_obuf; end RD_CTRL_ACK : sda_obuf 《= 1‘b0; RD : begin if (flag_low == 1’b1 && drive_cnt == 4‘d8) sda_obuf 《= 1’b1; else sda_obuf 《= sda_obuf; end NO_ACK : sda_obuf 《= sda_obuf; default : sda_obuf 《= 1‘b1; endcase end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) drive_cnt 《= 4‘d0; else case (c_state) IDLE : drive_cnt 《= 4’d0; START : drive_cnt 《= 4‘d0; CTRL : begin if (flag_low == 1’b1) if (drive_cnt 《 4‘d8) drive_cnt 《= drive_cnt + 1’b1; else drive_cnt 《= 4‘d0; else drive_cnt 《= drive_cnt; end CTRL_ACK : drive_cnt 《= 4’d0; HADDR : begin if (flag_low == 1‘b1) if (drive_cnt 《 4’d8) drive_cnt 《= drive_cnt + 1‘b1; else drive_cnt 《= 4’d0; else drive_cnt 《= drive_cnt; end HADDR_ACK : drive_cnt 《= 4‘d0; LADDR : begin if (flag_low == 1’b1) if (drive_cnt 《 4‘d8) drive_cnt 《= drive_cnt + 1’b1; else drive_cnt 《= 4‘d0; else drive_cnt 《= drive_cnt; end LADDR_ACK : drive_cnt 《= 4’d0; WR : begin if (flag_low == 1‘b1) if (drive_cnt 《 4’d8) drive_cnt 《= drive_cnt + 1‘b1; else drive_cnt 《= 4’d0; else drive_cnt 《= drive_cnt; end WR_ACK : drive_cnt 《= 4‘d0; STOP : drive_cnt 《= 4’d0; RD_START : drive_cnt 《= 4‘d0; RD_CTRL : begin if (flag_low == 1’b1) if (drive_cnt 《 4‘d8) drive_cnt 《= drive_cnt + 1’b1; else drive_cnt 《= 4‘d0; else drive_cnt 《= drive_cnt; end RD_CTRL_ACK : drive_cnt 《= 4’d0; RD : begin if (flag_high == 1‘b1 && drive_cnt 《 4’d8) drive_cnt 《= drive_cnt + 1‘b1; else if (flag_low == 1’b1 && drive_cnt == 4‘d8) drive_cnt 《= 4’d0; else drive_cnt 《= drive_cnt; end NO_ACK : drive_cnt 《= 4‘d0; default : drive_cnt 《= 4’d0; endcase end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) temp 《= 8’d0; else case (c_state) IDLE : temp 《= 8‘d0; START : begin if (flag_high == 1’b1) temp 《= {slave_addr, 1‘b0}; else temp 《= temp; end CTRL : begin if (flag_low == 1’b1 && drive_cnt 《 4‘d8) temp 《= temp 《《 1’b1; else temp 《= temp; end CTRL_ACK : begin if (flag_high == 1‘b1 && iic_sda == 1’b0) if (addr_sel == 1‘b1) temp 《= addr[15:8]; else temp 《= addr[7:0]; else temp 《= temp; end HADDR : begin if (flag_low == 1’b1 && drive_cnt 《 4‘d8) temp 《= temp 《《 1’b1; else temp 《= temp; end HADDR_ACK : begin if (flag_high == 1‘b1 && iic_sda == 1’b0) temp 《= addr[7:0]; else temp 《= temp; end LADDR : begin if (flag_low == 1‘b1 && drive_cnt 《 4’d8) temp 《= temp 《《 1‘b1; else temp 《= temp; end LADDR_ACK : begin if (flag_high == 1’b1 && iic_sda == 1‘b0) if (wren == 1’b1) temp 《= wdata; else temp 《= {slave_addr, 1‘b1}; else temp 《= temp; end WR : begin if (flag_low == 1’b1 && drive_cnt 《 4‘d8) temp 《= temp 《《 1’b1; else temp 《= temp; end WR_ACK : temp 《= 8‘d0; STOP : temp 《= 8’d0; RD_START : temp 《= temp; RD_CTRL : begin if (flag_low == 1‘b1 && drive_cnt 《 4’d8) temp 《= temp 《《 1‘b1; else temp 《= temp; end RD_CTRL_ACK : temp 《= 8’d0; RD : begin if (flag_high == 1‘b1 && drive_cnt 《 4’d8) temp 《= {temp[6:0], iic_sda}; else temp 《= temp; end NO_ACK : temp 《= 8‘d0; default : temp 《= 8’d0; endcase end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) iic_done 《= 1’b0; else if (c_state == STOP && flag_high == 1‘b1) iic_done 《= 1’b1; else iic_done 《= 1‘b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) rdata 《= 8‘d0; else if (c_state == RD && flag_low == 1’b1 && drive_cnt == 4‘d8) rdata 《= temp; else rdata 《= rdata; end endmodule

RTL仿真設計

在仿真時,需要外部提供ACK。如果直接仿真的話,由于外部沒有提供ACK,導致一直都會重復發送一小段。在仿真中,我們自己給出ACK的難度也比較大。

24LC64的廠商幫我們解決了這個問題,他們在造出這個芯片的同時,還設計出了可以幫助我們仿真調試的verliog模型。此模型放在msim-》24LC64_sim_module中,文件名字為24LC64.v。此模型的sda也是雙向IO,所以在硬件設計時,會在此IO上加上拉電阻,在仿真時,我們也要模擬上拉電阻。

仿真設計如下:

`timescale 1ns/1ps

module iic_drive_tb;

reg clk; reg rst_n; reg start_flag; reg [6:0] slave_addr; reg wren; reg addr_sel; reg [15:0] addr; reg [7:0] wdata; wire iic_scl; wire iic_sda; wire iic_done; wire [7:0] rdata; pullup(iic_sda); iic_drive iic_drive_inst(

.clk (clk), .rst_n (rst_n), .start_flag (start_flag), .slave_addr (slave_addr), .wren (wren), .addr_sel (addr_sel), .addr (addr), .wdata (wdata), .iic_scl (iic_scl), .iic_sda (iic_sda), .iic_done (iic_done), .rdata (rdata) ); M24LC64 M24LC64_inst( .A0 (1’b0), .A1 (1‘b0), .A2 (1’b0), .WP (1‘b0), .SDA (iic_sda), .SCL (iic_scl) );

initial clk = 1’b0; always # 10 clk = ~clk; initial begin rst_n = 1‘b0; start_flag = 1’b0; slave_addr = 7‘b1010_000; wren = 1’b0; addr_sel = 1‘b1; addr = 16’h5555; wdata = 8‘haa; # 201 rst_n = 1’b1; # 200; @ (posedge clk); # 2; start_flag = 1‘b1; wren = 1’b1; @ (posedge clk); # 2; start_flag = 1‘b0; @ (posedge iic_done); # 200; @ (posedge clk); # 2; start_flag = 1’b1; wren = 1‘b0; @ (posedge clk); # 2; start_flag = 1’b0; @ (posedge iic_done); # 200 $stop; end

endmodule

pullup(iic_sda):將iic_sda上加上拉電阻。

仿真時,在地址16’h5555(雖然高三位沒有用),寫入了8’ha;寫入完成后,又將此數據讀出。

當對24LC64進行寫入之后,它會進入到一個內部寫循環(和flash類似),在此期間,不接受任何指令。所以在仿真時,需要等待寫循環完成,時間為5ms(此參數在仿真模型中,可以進行修改)。

寫入的仿真圖如下:

66a1d5c8-a08a-11eb-8b86-12bb97331649.png

讀出的仿真圖如下:

66aed57a-a08a-11eb-8b86-12bb97331649.png

板級測試

上述的設計在使用時會有上游控制器進行控制,所以板級測試還需要設計控制器。在控制器中只需要某一個地址寫入數據,然后讀出即可。測試時,可以多次更換地址和數據,保證設計的正確性。

在測試時,將slave_addr、addr_sel、addr、wdata設置為定值。slave_addr為7’b1010_000,addr_sel為1’b1,addr為16’h5555,wdata為8’haa。

將上有控制器命名為test_ctrl,頂層命名為test。

66d59c46-a08a-11eb-8b86-12bb97331649.png

test_ctrl模塊采用狀態機設計實現。

66f91612-a08a-11eb-8b86-12bb97331649.png

test_ctrl的設計代碼如下:

module test_ctrl (

input wire clk, input wire rst_n, output reg start_flag, output reg wren, input wire iic_done, input wire [7:0] rdata);

localparam WR = 4‘b0001; localparam WR_WAIT = 4’b0010; localparam RD_WAIT = 4‘b0100; localparam TESTDONE = 4’b1000; reg [3:0] c_state; reg [3:0] n_state; always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) c_state 《= WR; else c_state 《= n_state; end always @ * begin case (c_state) WR : n_state = WR_WAIT; WR_WAIT : begin if (iic_done == 1’b1) n_state = RD_WAIT; else n_state = WR_WAIT; end RD_WAIT : begin if (iic_done == 1‘b1 && rdata == 8’haa) n_state = TESTDONE; else n_state = RD_WAIT; end TESTDONE : n_state = TESTDONE; default : n_state = WR; endcase end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) start_flag 《= 1’b0; else if ((c_state == WR) || (c_state == WR_WAIT && iic_done == 1‘b1)) start_flag 《= 1’b1; else start_flag 《= 1‘b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) wren 《= 1‘b0; else if (c_state == WR || c_state == WR_WAIT) wren 《= 1’b1; else wren 《= 1‘b0; end endmodule

test模塊負責將test_ctrl和iic_drive模塊進行例化連接。將test模塊設置為頂層。

test模塊設計代碼如下:

module test (

input wire clk, input wire rst_n, output wire iic_scl, inout wire iic_sda);

wire start_flag; wire wren; wire iic_done; wire [7:0] rdata; test_ctrl test_ctrl_inst(

.clk (clk), .rst_n (rst_n), .start_flag (start_flag), .wren (wren), .iic_done (iic_done), .rdata (rdata) ); iic_drive iic_drive_inst(

.clk (clk), .rst_n (rst_n), .start_flag (start_flag), .slave_addr (7’b1010_000), .wren (wren), .addr_sel (1‘b1), .addr (16’h5555), .wdata (8‘haa), .iic_scl (iic_scl), .iic_sda (iic_sda), .iic_done (iic_done), .rdata (rdata) ); endmodule

板級測試采用邏輯分析儀測試。

采樣時鐘選擇外部的50MHz,采樣深度選擇32K。

670614de-a08a-11eb-8b86-12bb97331649.png

觸發位置選擇post。

672a69e2-a08a-11eb-8b86-12bb97331649.png

將iic_sda、iic_scl、test_ctrl模塊中的RD_WAIT和TESTDONE兩個狀態,共計四個信號進行觀測。

676d976c-a08a-11eb-8b86-12bb97331649.png

將RD_WAIT設置為上升沿觸發。進入此狀態時,剛剛寫入完成。

679182f8-a08a-11eb-8b86-12bb97331649.png

點擊觸發后,按一下復位鍵。

67b19782-a08a-11eb-8b86-12bb97331649.png

從波形圖中,可以分析出,在地址16’h5555中寫入了8’haa。

將RD_WAIT設置為donot care,將TESTDONE設置為上升沿,此時讀出剛剛完成。

67d30688-a08a-11eb-8b86-12bb97331649.png

觸發后,按一下復位按鍵。

67f5efc2-a08a-11eb-8b86-12bb97331649.png

可以看出在地址16’h5555的位置,讀出aa。

注意發送時,是在SCL的低電平的正中間;接收是在SCL的高電平的正中間;觀看波形時,要和標準的時序圖進行對比。

證明設計正確。
編輯:lyn

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FPGA
    +關注

    關注

    1660

    文章

    22411

    瀏覽量

    636265
  • EEPROM
    +關注

    關注

    9

    文章

    1137

    瀏覽量

    86029
  • IIC
    IIC
    +關注

    關注

    11

    文章

    309

    瀏覽量

    40632

原文標題:FPGA零基礎學習精選 | IIC協議驅動設計

文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    IIC的正確用法

    的上拉電阻可以使能,這樣就省去了外部的上拉電阻,AVR單片機,就是使能內部上拉電阻。但是如果是使用單片機的引腳模擬IIC協議,就得看你的單片機引腳是否支持漏極開路模式或者上拉模式,一般推挽模式的輸出
    發表于 01-21 07:28

    使用Aurora 6466b協議實現AMD UltraScale+ FPGA與AMD Versal自適應SoC的對接

    在本博客中,我們將介紹使用 Aurora 6466b 協議實現 AMD UltraScale+ FPGA 與 AMD Versal 自適應 SoC 的對接。我們還將涵蓋有關 IP 配置、FPGA 之間的連接、時鐘設置以及復位拓撲
    的頭像 發表于 01-13 14:04 ?3405次閱讀
    使用Aurora 6466b<b class='flag-5'>協議</b>實現AMD UltraScale+ <b class='flag-5'>FPGA</b>與AMD Versal自適應SoC的對接

    RA MCU眾測寶典 | IIC之【RA2E1】IIC通信的OLED顯示

    ,到OLED驅動函數編寫、圖像文字取模與顯示,一起感受僅用SDA和SCL兩條線,就能實現設備間數據交互與可視化的便捷魅力。開啟寶典簡介IIC通信協議IIC(Inte
    的頭像 發表于 01-01 10:04 ?1810次閱讀
    RA MCU眾測寶典 | <b class='flag-5'>IIC</b>之【RA2E1】<b class='flag-5'>IIC</b>通信的OLED顯示

    通信協議IIC與SPI最全對比分析

    現今,在低端數字通信應用領域,我們隨處可見IIC(Inter-Integrated Circuit)和 SPI(Serial Peripheral Interface)的身影。原因是這兩種通信協議
    發表于 12-10 06:04

    FPGA實現基于SPI協議的Flash驅動控制芯片擦除

    本篇博客具體包括SPI協議的基本原理、模式選擇以及時序邏輯要求,采用FPGA(EPCE4),通過SPI通信協議,對flash(W25Q16BV)存儲的固化程序進行芯片擦除操作。
    的頭像 發表于 12-02 10:00 ?2572次閱讀
    <b class='flag-5'>FPGA</b>實現基于SPI<b class='flag-5'>協議</b>的Flash<b class='flag-5'>驅動</b>控制芯片擦除

    Xilinx FPGA串行通信協議介紹

    Xilinx FPGA因其高性能和低延遲,常用于串行通信接口設計。本文深入分析了Aurora、PCI Express和Serial RapidIO這三種在Xilinx系統設計中關鍵的串行通信協議。介紹了它們的特性、優勢和應用場景,以及如何在不同需求下選擇合適的
    的頭像 發表于 11-14 15:02 ?2533次閱讀
    Xilinx <b class='flag-5'>FPGA</b>串行通信<b class='flag-5'>協議</b>介紹

    如何使用FPGA實現SRIO通信協議

    本例程詳細介紹了如何在FPGA上實現Serial RapidIO(SRIO)通信協議,并通過Verilog語言進行編程設計。SRIO作為一種高速、低延遲的串行互連技術,在高性能計算和嵌入式系統中廣
    的頭像 發表于 11-12 14:38 ?5765次閱讀
    如何使用<b class='flag-5'>FPGA</b>實現SRIO通信<b class='flag-5'>協議</b>

    開源RISC-V處理器(蜂鳥E203)學習(二)修改FPGA綜合環境(移植到自己的Xilinx FPGA板卡)

    1.簡述 首先感謝芯來開源了蜂鳥E203 risc-v處理器,提供了比較完整的工程環境、配套的軟硬件。但是配套的FPGA板卡實在太貴,對于自費學習的來說是不小的學習成本,而且我也認為完備環境
    發表于 10-31 08:46

    基于hbirdv2-sdk的iic 0.96寸lcd調試

    一.前言 簡介: 基于hbird-sdk的硬件IIC控制 0.96寸lcd. 二.工程結構 硬件: mcu200t fpga開發板 軟件結構: 主要由Makefile main.c
    發表于 10-30 06:20

    請問IIC的設備驅動兼容SMbus協議嗎?

    請問IIC的設備驅動兼容SMbus協議么?
    發表于 09-29 09:53

    FPGA在機器學習中的具體應用

    隨著機器學習和人工智能技術的迅猛發展,傳統的中央處理單元(CPU)和圖形處理單元(GPU)已經無法滿足高效處理大規模數據和復雜模型的需求。FPGA(現場可編程門陣列)作為一種靈活且高效的硬件加速平臺
    的頭像 發表于 07-16 15:34 ?2888次閱讀

    開源電機驅動,免費直播學習!

    開源電機驅動,免費直播學習!
    的頭像 發表于 06-13 10:07 ?1712次閱讀
    開源電機<b class='flag-5'>驅動</b>,免費直播<b class='flag-5'>學習</b>!

    adxl382的iic讀取數據正常,但是spi似乎完全沒有響應,怎么解決?

    adxl382的iic讀取數據正常,但是spi似乎完全沒有響應,連id都讀不到,邏輯分析儀上看協議也正確,請問如何調試呢?
    發表于 04-29 08:08

    ZYNQ FPGA的PS端IIC設備接口使用

    zynq系列中的FPGA,都會自帶兩個iic設備,我們直接調用其接口函數即可運用。使用xilinx官方提供的庫函數,開發起來方便快捷。
    的頭像 發表于 04-17 11:26 ?2137次閱讀
    ZYNQ <b class='flag-5'>FPGA</b>的PS端<b class='flag-5'>IIC</b>設備接口使用

    【瑞薩RA2L1入門學習】+ OLED驅動

    ://bbs.elecfans.com/jishu_2474502_1_1.html 這篇帖子,我記錄了如何創建開發環境,此次在這個工程基礎之上進行IIC的配置,驅動移植。 2、添加IIC驅動
    發表于 03-09 19:25