精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

FPGA基礎(chǔ)學(xué)習(xí)IIC協(xié)議驅(qū)動(dòng)設(shè)計(jì)詳解

電子工程師 ? 來(lái)源:CSDN技術(shù)社區(qū) ? 作者: FPGA技術(shù)江湖 ? 2021-05-02 09:15 ? 次閱讀

本系列將帶來(lái)FPGA的系統(tǒng)性學(xué)習(xí),從最基本的數(shù)字電路基礎(chǔ)開(kāi)始,最詳細(xì)操作步驟,最直白的言語(yǔ)描述,手把手的“傻瓜式”講解,讓電子信息通信類專業(yè)學(xué)生、初入職場(chǎng)小白及打算進(jìn)階提升的職業(yè)開(kāi)發(fā)者都可以有系統(tǒng)性學(xué)習(xí)的機(jī)會(huì)。

系統(tǒng)性的掌握技術(shù)開(kāi)發(fā)以及相關(guān)要求,對(duì)個(gè)人就業(yè)以及職業(yè)發(fā)展都有著潛在的幫助,希望對(duì)大家有所幫助。后續(xù)會(huì)陸續(xù)更新 Xilinx 的 Vivado、ISE 及相關(guān)操作軟件的開(kāi)發(fā)的相關(guān)內(nèi)容,學(xué)習(xí)FPGA設(shè)計(jì)方法及設(shè)計(jì)思想的同時(shí),實(shí)操結(jié)合各類操作軟件,會(huì)讓你在技術(shù)學(xué)習(xí)道路上無(wú)比的順暢,告別技術(shù)學(xué)習(xí)小BUG卡破腦殼,告別目前忽悠性的培訓(xùn)誘導(dǎo),真正的去學(xué)習(xí)去實(shí)戰(zhàn)應(yīng)用,這種快樂(lè)試試你就會(huì)懂的。話不多說(shuō),上貨。

本篇實(shí)現(xiàn)基于叁芯智能科技的SANXIN -B01 FPGA開(kāi)發(fā)板,以下為配套的教程,如有入手開(kāi)發(fā)板,可以登錄官方淘寶店購(gòu)買,還有配套的學(xué)習(xí)視頻。

I2C 即 Inter-Integrated Circuit(集成電路總線),這種總線類型是由飛利浦半導(dǎo)體公司在八十年代初設(shè)計(jì)出來(lái)的一種簡(jiǎn)單、雙向、二線制、同步串行總線,主要是用來(lái)連接整體電路(ICS) ,I2C是一種多向控制總線,也就是說(shuō)多個(gè)芯片可以連接到同一總線結(jié)構(gòu)下,同時(shí)每個(gè)芯片都可以作為實(shí)時(shí)數(shù)據(jù)傳輸?shù)目刂圃础_@種方式簡(jiǎn)化了信號(hào)傳輸總線接口。

I2C 總線是一種串行數(shù)據(jù)總線,只有二根信號(hào)線,一根是雙向的數(shù)據(jù)線 SDA,另一根是時(shí)鐘線 SCL,兩條線可以掛多個(gè)設(shè)備。 I2C 設(shè)備(絕大多數(shù))里有個(gè)固化的地址,只有在兩條線上傳輸?shù)闹档扔?I2C 設(shè)備的固化地址時(shí),其才會(huì)作出響應(yīng)。通常我們?yōu)榱朔奖惆?I2C 設(shè)備分為主設(shè)備和從設(shè)備,基本上誰(shuí)控制時(shí)鐘線(即控制SCL的電平高低變換)誰(shuí)就是主設(shè)備。

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

A段為空閑段,在不進(jìn)行數(shù)據(jù)傳輸時(shí),SCL 和 SDA 保持為高電平。

B段為起始段(START CONDITION),在進(jìn)行任何數(shù)據(jù)傳輸前,都應(yīng)該先有一個(gè)起始段。當(dāng) SCL 為高電平期間,SDA有一個(gè)從高到低的變化視為起始段。

C段為結(jié)束段(STOP CONDITION),在數(shù)據(jù)傳輸結(jié)束后,都應(yīng)該有一個(gè)結(jié)束段。當(dāng)SCL為高電平期間,SDA有一個(gè)從低到高的變化視為結(jié)束段。

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

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

SDA的數(shù)據(jù)線是雙向傳輸?shù)摹T谧x取時(shí),SDA 從從設(shè)備到主設(shè)備;在寫(xiě)入時(shí),SDA從主設(shè)備到從設(shè)備。

在 IIC 傳輸時(shí),為了保證通信的成功。發(fā)送方每發(fā)送8 bit 數(shù)據(jù)后,要求接收方回復(fù)一個(gè)應(yīng)答位。

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

應(yīng)答位為低電平有效。

EEPROM(24LC64)簡(jiǎn)介

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

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

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

A0、A1、A2為用戶可配置的芯片選擇端。該管腳一般都連接電源或者地,在通信時(shí),24LC64作為從機(jī),主機(jī)在發(fā)送的命令中存在地址號(hào),當(dāng)?shù)刂诽?hào)與A0、A1、A2管腳的電平相同時(shí),該芯片視為被選中。

SDA和SCL為IIC協(xié)議中的時(shí)鐘線和數(shù)據(jù)線。

WP為寫(xiě)保護(hù)端,當(dāng)連接高電平時(shí),該芯片只能夠被讀,不能夠?qū)懭耄划?dāng)連接低電平或者空置時(shí),該芯片可以被讀寫(xiě)。

24LC64的寫(xiě)入方式有兩種:?jiǎn)巫止?jié)寫(xiě)入和多字節(jié)寫(xiě)入。

單字節(jié)寫(xiě)入時(shí)序圖:

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

在控制字節(jié)中,1010為EEPROM的標(biāo)號(hào);A2、A1、A0為主機(jī)發(fā)送的地址號(hào),接收設(shè)備接收到此信息后,會(huì)與本身的A2、A1、A0相對(duì)比,相同時(shí),視為被選中;最后1bit,0表示寫(xiě)入、1表示讀出。

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

多字節(jié)寫(xiě)入時(shí)序圖:

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

DATA BYTE 0被寫(xiě)入到指定的地址中,后續(xù)的數(shù)據(jù)寫(xiě)入到指定地址的后續(xù)地址中。EEPROM中每32個(gè)字節(jié)分為一塊,寫(xiě)入時(shí)不能跨塊寫(xiě)入。

24LC64的讀出方式有三種:當(dāng)前地址單字節(jié)讀、任意地址單字節(jié)讀和任意地址多字節(jié)讀。

當(dāng)前地址單字節(jié)讀的時(shí)序圖如下:

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

當(dāng)主機(jī)接收24LC64的數(shù)據(jù)后,不發(fā)送ACK,而是發(fā)送高電平。

任意地址單字節(jié)讀取時(shí)序圖,如下:

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

首先寫(xiě)入指定地址,然后按照當(dāng)前地址讀即可。

任意地址多字節(jié)讀,時(shí)序如下:

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

任意地址多字節(jié)讀就是在任意地址單字節(jié)讀的時(shí)序中,接收到從機(jī)發(fā)送的數(shù)據(jù)后,不要發(fā)送NO ACK而是發(fā)送ACK,此時(shí)從機(jī)就會(huì)繼續(xù)發(fā)送后續(xù)地址的數(shù)據(jù),直到讀取的長(zhǎng)度符合設(shè)計(jì)要求,然后發(fā)送NO ACK以及STOP結(jié)束傳輸。

下圖為24LC64的時(shí)序參數(shù)圖。

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

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

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

根據(jù)時(shí)序參數(shù),決定將IIC的速率定為50KHz。發(fā)送時(shí),數(shù)據(jù)改變?cè)赟CL的低電平的正中間;讀取時(shí),在SCL高電平的正中間進(jìn)行讀取。

IIC一般用在配置或者讀取少量的信息,對(duì)于速率來(lái)說(shuō)要求相對(duì)較低。

硬件介紹

在開(kāi)發(fā)板上,搭載了一個(gè)24LC64。

電路原理圖如下:

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

A0、A1、A2都被接地,主機(jī)再發(fā)送控制字節(jié)時(shí),需要將此三位發(fā)送低電平。

WP管腳被接地,24LC64的整個(gè)片子都可以被讀寫(xiě)。

雙向IO介紹

在IIC協(xié)議中,SDA作為數(shù)據(jù)線使用,每一個(gè)SCL的脈沖,傳遞一bit的數(shù)據(jù)。在讀取時(shí),SDA由24LC64發(fā)送,F(xiàn)PGA接收;在寫(xiě)入時(shí),SDA由FPGA發(fā)送,24LC64接收。所以IIC協(xié)議只能實(shí)現(xiàn)半雙工通信。

在硬件電路中,一般是通過(guò)三態(tài)門來(lái)進(jìn)行控制雙向IO。

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

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

三態(tài)門有三個(gè)端子,分別是輸入端(obuf),輸出端(SDA)和控制端(out_en)。工作方式為:當(dāng)out_en有效時(shí),obuf的值就可以輸出到SDA;當(dāng)out_en無(wú)效時(shí),obuf的值不能輸出到SDA,那么三態(tài)門就會(huì)輸出高阻態(tài)。

高阻態(tài),字節(jié)理解為阻值特別大,也可以理解為斷開(kāi)。高阻態(tài)與任何電平值相連接,所連接的線上的電平值都是和對(duì)方保持一致。在此可以延伸一下:所有的端口都是輸出,包括輸入端口在內(nèi),也會(huì)對(duì)外呈現(xiàn)出一種狀態(tài),所以輸入端口對(duì)外呈現(xiàn)的狀態(tài)就是高阻態(tài)。對(duì)于雙向IO來(lái)說(shuō),輸出時(shí),正常輸出即可;輸入時(shí),就要呈現(xiàn)為高阻態(tài)。

設(shè)計(jì)要求

設(shè)計(jì)任意地址的單字節(jié)讀寫(xiě)控制器。大多數(shù)是配置或者讀取少量的信息,對(duì)于多字節(jié)的讀寫(xiě),可以采用多次單字節(jié)的讀寫(xiě)完成。

設(shè)計(jì)分析

24LC64的尋址范圍為8K,地址bit共計(jì)13bit,所以需要高5bit和低8bit。在有些兼容IIC協(xié)議接口的器件中,尋址范圍較少,8bit足夠表示,所以在設(shè)計(jì)時(shí),設(shè)計(jì)出適合兩種尋址方式的驅(qū)動(dòng)。

SCL的實(shí)現(xiàn)采用計(jì)數(shù)器控制,根據(jù)規(guī)定好的頻率,在合適的地方進(jìn)行拉高或者拉低。由于SDA的變化都發(fā)生在SCL為高或者為低的中間,所以還需要產(chǎn)生對(duì)應(yīng)的脈沖。

SDA根據(jù)協(xié)議和對(duì)應(yīng)的脈沖進(jìn)行發(fā)送對(duì)應(yīng)的協(xié)議段。

架構(gòu)設(shè)計(jì)和信號(hào)說(shuō)明

本模塊命名為iic_drive。

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

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

暫不連接的端口,在后續(xù)測(cè)試時(shí),會(huì)有其他模塊來(lái)控制或者讀取。

iic_drive設(shè)計(jì)實(shí)現(xiàn)

iic_scl采用計(jì)數(shù)器的方式實(shí)現(xiàn)。外部的時(shí)鐘為50MHz,iic_scl為100KHz,所以計(jì)數(shù)器需要記錄500個(gè)時(shí)鐘周期。

計(jì)數(shù)器由cnt_en控制。cnt_en有效時(shí),cnt從0到500不斷循環(huán);當(dāng)cnt_en無(wú)效時(shí),cnt保持等于0。

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

其他信號(hào)采用狀態(tài)機(jī)作為輸出。out_en為三態(tài)門控制信號(hào),sda_obuf為三態(tài)門輸出端,drive_cnt為發(fā)送或者接收的計(jì)數(shù)器,temp為發(fā)送或者接收的中間寄存器

狀態(tài)轉(zhuǎn)移圖如下:

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

設(shè)計(jì)代碼為:

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仿真設(shè)計(jì)

在仿真時(shí),需要外部提供ACK。如果直接仿真的話,由于外部沒(méi)有提供ACK,導(dǎo)致一直都會(huì)重復(fù)發(fā)送一小段。在仿真中,我們自己給出ACK的難度也比較大。

24LC64的廠商幫我們解決了這個(gè)問(wèn)題,他們?cè)谠斐鲞@個(gè)芯片的同時(shí),還設(shè)計(jì)出了可以幫助我們仿真調(diào)試的verliog模型。此模型放在msim-》24LC64_sim_module中,文件名字為24LC64.v。此模型的sda也是雙向IO,所以在硬件設(shè)計(jì)時(shí),會(huì)在此IO上加上拉電阻,在仿真時(shí),我們也要模擬上拉電阻。

仿真設(shè)計(jì)如下:

`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上加上拉電阻。

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

當(dāng)對(duì)24LC64進(jìn)行寫(xiě)入之后,它會(huì)進(jìn)入到一個(gè)內(nèi)部寫(xiě)循環(huán)(和flash類似),在此期間,不接受任何指令。所以在仿真時(shí),需要等待寫(xiě)循環(huán)完成,時(shí)間為5ms(此參數(shù)在仿真模型中,可以進(jìn)行修改)。

寫(xiě)入的仿真圖如下:

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

讀出的仿真圖如下:

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

板級(jí)測(cè)試

上述的設(shè)計(jì)在使用時(shí)會(huì)有上游控制器進(jìn)行控制,所以板級(jí)測(cè)試還需要設(shè)計(jì)控制器。在控制器中只需要某一個(gè)地址寫(xiě)入數(shù)據(jù),然后讀出即可。測(cè)試時(shí),可以多次更換地址和數(shù)據(jù),保證設(shè)計(jì)的正確性。

在測(cè)試時(shí),將slave_addr、addr_sel、addr、wdata設(shè)置為定值。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模塊采用狀態(tài)機(jī)設(shè)計(jì)實(shí)現(xiàn)。

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

test_ctrl的設(shè)計(jì)代碼如下:

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模塊負(fù)責(zé)將test_ctrl和iic_drive模塊進(jìn)行例化連接。將test模塊設(shè)置為頂層。

test模塊設(shè)計(jì)代碼如下:

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

板級(jí)測(cè)試采用邏輯分析儀測(cè)試。

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

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

觸發(fā)位置選擇post。

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

將iic_sda、iic_scl、test_ctrl模塊中的RD_WAIT和TESTDONE兩個(gè)狀態(tài),共計(jì)四個(gè)信號(hào)進(jìn)行觀測(cè)。

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

將RD_WAIT設(shè)置為上升沿觸發(fā)。進(jìn)入此狀態(tài)時(shí),剛剛寫(xiě)入完成。

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

點(diǎn)擊觸發(fā)后,按一下復(fù)位鍵。

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

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

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

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

觸發(fā)后,按一下復(fù)位按鍵。

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

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

注意發(fā)送時(shí),是在SCL的低電平的正中間;接收是在SCL的高電平的正中間;觀看波形時(shí),要和標(biāo)準(zhǔn)的時(shí)序圖進(jìn)行對(duì)比。

證明設(shè)計(jì)正確。
編輯:lyn

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • FPGA
    +關(guān)注

    關(guān)注

    1626

    文章

    21671

    瀏覽量

    601895
  • EEPROM
    +關(guān)注

    關(guān)注

    9

    文章

    1010

    瀏覽量

    81411
  • IIC
    IIC
    +關(guān)注

    關(guān)注

    11

    文章

    300

    瀏覽量

    38277

原文標(biāo)題:FPGA零基礎(chǔ)學(xué)習(xí)精選 | IIC協(xié)議驅(qū)動(dòng)設(shè)計(jì)

文章出處:【微信號(hào):HXSLH1010101010,微信公眾號(hào):FPGA技術(shù)江湖】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    FPGA加速深度學(xué)習(xí)模型的案例

    FPGA(現(xiàn)場(chǎng)可編程門陣列)加速深度學(xué)習(xí)模型是當(dāng)前硬件加速領(lǐng)域的一個(gè)熱門研究方向。以下是一些FPGA加速深度學(xué)習(xí)模型的案例: 一、基于FPGA
    的頭像 發(fā)表于 10-25 09:22 ?146次閱讀

    IIC信號(hào)線需要增加上拉電阻,是因?yàn)?b class='flag-5'>IIC的IO是什么

    IIC通信中扮演著至關(guān)重要的角色,而上拉電阻的加入則是實(shí)現(xiàn)這種通信方式有效性和可靠性的關(guān)鍵。以下是對(duì)此現(xiàn)象的介紹: 一、IIC通信與開(kāi)漏輸出 IIC通信協(xié)議是一種廣泛應(yīng)用的串行通信
    的頭像 發(fā)表于 10-06 15:50 ?477次閱讀

    LwIP協(xié)議棧源碼詳解—TCP/IP協(xié)議的實(shí)現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《LwIP協(xié)議棧源碼詳解—TCP/IP協(xié)議的實(shí)現(xiàn).pdf》資料免費(fèi)下載
    發(fā)表于 07-03 11:22 ?3次下載

    【GD32F303紅楓派開(kāi)發(fā)板使用手冊(cè)】第二十二講 IIC-IIC OLED顯示實(shí)驗(yàn)

    通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容: ?OLED驅(qū)動(dòng)原理 ?IIC驅(qū)動(dòng)OLED顯示操作 22.2 實(shí)驗(yàn)原理 OLED模塊的驅(qū)動(dòng)芯片為SSD1306,
    的頭像 發(fā)表于 06-22 09:59 ?585次閱讀
    【GD32F303紅楓派開(kāi)發(fā)板使用手冊(cè)】第二十二講  <b class='flag-5'>IIC-IIC</b> OLED顯示實(shí)驗(yàn)

    FPGA學(xué)習(xí)筆記-關(guān)于FPGA資源

    FPGA學(xué)習(xí)。 在學(xué)習(xí)中才發(fā)現(xiàn),FPGA遠(yuǎn)不是門電路那么簡(jiǎn)單。FPGA中有各種需要的資源,比如門電路、存儲(chǔ)單元、片內(nèi)RAM、嵌入式乘法器、
    發(fā)表于 05-22 18:27

    面試遇到再不怕了:IIC協(xié)議

    點(diǎn)擊上方藍(lán)字關(guān)注我們I2C(Inter-IntegratedCircuit),有時(shí)也稱為IIC或TWI(Two-WireInterface),是一種用于低速度串行通信的總線協(xié)議,主要用于連接微控制器
    的頭像 發(fā)表于 04-29 08:10 ?7789次閱讀
    面試遇到再不怕了:<b class='flag-5'>IIC</b><b class='flag-5'>協(xié)議</b>

    FPGA學(xué)習(xí)筆記-入門

    大概10年前,大學(xué)同學(xué)建議我學(xué)習(xí)DSP。當(dāng)因?yàn)楣ぷ髅Γ仓皇呛?jiǎn)單學(xué)習(xí)了DSP的一些基礎(chǔ)知識(shí),沒(méi)有進(jìn)一步深入學(xué)習(xí)和時(shí)間。結(jié)果現(xiàn)在,好像DSP已經(jīng)不再是主流了,現(xiàn)在有了FPGA。 現(xiàn)在想想
    發(fā)表于 04-09 10:55

    FPGA初步學(xué)習(xí)印象

    了。FPGA出現(xiàn)后,也想學(xué)習(xí)學(xué)習(xí)這方面的知識(shí)。抱著先簡(jiǎn)單學(xué)習(xí)下的態(tài)度,在網(wǎng)上找資料看看。感覺(jué)FPGA,就像是利用芯片內(nèi)部資源,自由組合門電路
    發(fā)表于 04-09 09:46

    fpga學(xué)習(xí)需要具備哪些課程

    FPGA(Field Programmable Gate Array)學(xué)習(xí)需要具備一系列的課程知識(shí)和實(shí)踐技能
    的頭像 發(fā)表于 03-14 15:51 ?1131次閱讀

    FPGA實(shí)現(xiàn)IIC協(xié)議的設(shè)計(jì)

    今天給大家?guī)?lái)的是IIC通信,IIC協(xié)議應(yīng)用非常廣泛,例如與MPU6050進(jìn)行通信,配置OV5640攝像頭、驅(qū)動(dòng)OLED屏幕等等,都需要使用到IIC
    的頭像 發(fā)表于 03-04 10:49 ?1182次閱讀
    <b class='flag-5'>FPGA</b>實(shí)現(xiàn)<b class='flag-5'>IIC</b><b class='flag-5'>協(xié)議</b>的設(shè)計(jì)

    【有那么點(diǎn)詳細(xì)的CW32學(xué)習(xí)筆記】IIC接口-主機(jī)發(fā)送

    OLED屏幕,如果你對(duì)如何編寫(xiě)各種模塊的驅(qū)動(dòng)代碼束手無(wú)策,那本系列教程的IIC章節(jié)或許能讓你受益匪淺。 Inter-Integrated Circuit Bus,集成電路總線,簡(jiǎn)稱IIC總線。這是一種
    的頭像 發(fā)表于 01-11 09:53 ?573次閱讀
    【有那么點(diǎn)詳細(xì)的CW32<b class='flag-5'>學(xué)習(xí)</b>筆記】<b class='flag-5'>IIC</b>接口-主機(jī)發(fā)送

    【有那么點(diǎn)詳細(xì)的CW32學(xué)習(xí)筆記】IIC接口-OLED驅(qū)動(dòng)

    上一章我們講解了IIC的通信流程以及通信代碼,這一章就以市面上常見(jiàn)的IIC接口模塊——OLED屏為例教學(xué)一下IIC接口的驅(qū)動(dòng)怎么寫(xiě)。 第一步當(dāng)然是搞清楚自己使用的OLED屏幕用的是什么
    的頭像 發(fā)表于 01-11 09:49 ?695次閱讀
    【有那么點(diǎn)詳細(xì)的CW32<b class='flag-5'>學(xué)習(xí)</b>筆記】<b class='flag-5'>IIC</b>接口-OLED<b class='flag-5'>驅(qū)動(dòng)</b>

    【CW32學(xué)習(xí)筆記】IIC接口-主機(jī)發(fā)送

    接著就是比較重要的部分了,IIC接口的收發(fā)并不是全自動(dòng)的,因?yàn)橐粋€(gè)完整的通信不僅包括發(fā)數(shù)據(jù)(地址、數(shù)據(jù)什么的),還包含收數(shù)據(jù)(啥也不干也得接收ACK信號(hào)),所以IIC通信的每個(gè)部分基本上都是收發(fā)易位的過(guò)程,IIC外設(shè)并不會(huì)自動(dòng)完
    的頭像 發(fā)表于 01-10 15:59 ?607次閱讀
    【CW32<b class='flag-5'>學(xué)習(xí)</b>筆記】<b class='flag-5'>IIC</b>接口-主機(jī)發(fā)送

    IIC總線的FPGA實(shí)現(xiàn)說(shuō)明

    DE2_TV中,有關(guān)于寄存器的配置的部分,采用的方法是通過(guò)IIC的功能,這里對(duì)IIC總線的FPGA實(shí)現(xiàn)做個(gè)說(shuō)明。
    的頭像 發(fā)表于 01-05 10:16 ?1017次閱讀
    <b class='flag-5'>IIC</b>總線的<b class='flag-5'>FPGA</b>實(shí)現(xiàn)說(shuō)明

    學(xué)習(xí)FPGA必須知道的社區(qū)

    學(xué)習(xí)FPGA必須知道的社區(qū)
    的頭像 發(fā)表于 01-03 17:51 ?358次閱讀
    <b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>FPGA</b>必須知道的社區(qū)