中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

找事做網(wǎng)站怎么弄百度網(wǎng)頁(yè)版登錄入口官網(wǎng)

找事做網(wǎng)站怎么弄,百度網(wǎng)頁(yè)版登錄入口官網(wǎng),早8晚5雙休的工作,汅app下載大全20221、概括 前文對(duì)IIC的時(shí)序做了詳細(xì)的講解,還有不懂的可以獲取TI的IIC數(shù)據(jù)手冊(cè)查看原理。通過(guò)手冊(cè)需要知道的是IIC讀、寫(xiě)數(shù)據(jù)都是以字節(jié)為單位,每次操作后接收方都需要進(jìn)行應(yīng)答。主機(jī)向從機(jī)寫(xiě)入數(shù)據(jù)后,從機(jī)接收數(shù)據(jù),需要把總線(xiàn)拉低來(lái)…

1、概括

??前文對(duì)IIC的時(shí)序做了詳細(xì)的講解,還有不懂的可以獲取TI的IIC數(shù)據(jù)手冊(cè)查看原理。通過(guò)手冊(cè)需要知道的是IIC讀、寫(xiě)數(shù)據(jù)都是以字節(jié)為單位,每次操作后接收方都需要進(jìn)行應(yīng)答。主機(jī)向從機(jī)寫(xiě)入數(shù)據(jù)后,從機(jī)接收數(shù)據(jù),需要把總線(xiàn)拉低來(lái)告知主機(jī),前面發(fā)送的數(shù)據(jù)已經(jīng)被接收。主機(jī)在讀取從機(jī)數(shù)據(jù)后,如果還需要繼續(xù)讀取數(shù)據(jù),就要對(duì)從機(jī)做出應(yīng)答,否則不應(yīng)答。

??另一個(gè)需要注意的是數(shù)據(jù)在時(shí)鐘的低電平中間進(jìn)行賦值,數(shù)據(jù)線(xiàn)在時(shí)鐘線(xiàn)的高電平期間狀態(tài)不能發(fā)生變化。這是因?yàn)樵跁r(shí)鐘線(xiàn)高電平期間,數(shù)據(jù)線(xiàn)從高電平變?yōu)榈碗娖?#xff0c;從機(jī)會(huì)認(rèn)為主機(jī)發(fā)送了起始位,數(shù)據(jù)線(xiàn)從低電平變?yōu)楦唠娖?#xff0c;從機(jī)會(huì)認(rèn)為主句發(fā)送停止位。

??在起始位和停止位之間,可以存在任意字節(jié)長(zhǎng)度的操作,也就是說(shuō)從機(jī)寄存器地址和寄存器數(shù)據(jù)的寬度都沒(méi)有限制,根據(jù)具體的芯片確定。其實(shí)很好理解,比如EEPROM支持單字節(jié)的讀寫(xiě)操作和突發(fā)的頁(yè)讀寫(xiě)操作,這就是上述原因的結(jié)果。還有部分芯片的寄存器地址可能是3個(gè)字節(jié),讀寫(xiě)的數(shù)據(jù)也是幾個(gè)字節(jié),這也是可以的。

??使用FPGA接口實(shí)現(xiàn)IIC的難度會(huì)比UART和SPI高那么一點(diǎn),原因在于雙向IO的控制。雙向IO一般使用三態(tài)門(mén)實(shí)現(xiàn),當(dāng)然xilinx這類(lèi)器件還可以使用IOBUFR這種原語(yǔ)實(shí)現(xiàn),會(huì)比使能簡(jiǎn)單很多,但是本文設(shè)計(jì)的是通用模塊,沒(méi)有平臺(tái)限制的代碼,所以不會(huì)使用原語(yǔ)。

??網(wǎng)上關(guān)于FPGA的IIC控制器代碼還是挺多的,但是基本上對(duì)寄存器地址、數(shù)據(jù)長(zhǎng)度都有限制,而且不支持突發(fā)讀寫(xiě),如果需要這些功能還是需要獨(dú)立開(kāi)發(fā),所以本文就打算設(shè)計(jì)一個(gè)支持寄存器地址長(zhǎng)度可變、數(shù)據(jù)長(zhǎng)度可變、支持突發(fā)讀、寫(xiě)的接口模塊,且沒(méi)有平臺(tái)限制,一次解決所有問(wèn)題。

??最后在eeprom上驗(yàn)證單字節(jié)讀寫(xiě)和突發(fā)讀寫(xiě)。

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

??首先通過(guò)幾個(gè)時(shí)序圖來(lái)具體分析一下單字節(jié)、多字節(jié)讀寫(xiě)時(shí)序和多字節(jié)的地址讀寫(xiě)時(shí)序,進(jìn)而總結(jié)出設(shè)計(jì)思路。

??如下圖所示,是eeprom芯片的單字節(jié)寫(xiě)時(shí)序,該時(shí)序每次只寫(xiě)入單個(gè)存儲(chǔ)單元的單字節(jié)數(shù)據(jù),所以依次發(fā)送起始位、器件地址、寫(xiě)指示位、從機(jī)應(yīng)答、寫(xiě)入地址、從機(jī)應(yīng)答、寫(xiě)入數(shù)據(jù)、從機(jī)應(yīng)答、停止位即可。

在這里插入圖片描述

圖1 某eeprom的單字節(jié)寫(xiě)時(shí)序

??下圖是該eeprom實(shí)現(xiàn)頁(yè)寫(xiě)的時(shí)序,頁(yè)寫(xiě)與sdram這些的突發(fā)寫(xiě)本質(zhì)是一樣的,就是發(fā)送起始位值的地址,后面連續(xù)輸入后續(xù)地址的數(shù)據(jù)即可。與上圖的區(qū)別是在第一次寫(xiě)入數(shù)據(jù),從機(jī)應(yīng)答之后,主機(jī)不發(fā)送停止位,而是繼續(xù)寫(xiě)入數(shù)據(jù),便可以向從機(jī)的下一個(gè)地址寫(xiě)入數(shù)據(jù),從機(jī)應(yīng)答之后繼續(xù)寫(xiě)入數(shù)據(jù),直到寫(xiě)入指定個(gè)數(shù)的數(shù)據(jù)且從機(jī)應(yīng)答之后,發(fā)送停止位結(jié)束寫(xiě)入。

??頁(yè)寫(xiě)入表面上看只節(jié)省了發(fā)送起始位、器件地址、寄存器地址的時(shí)間,但其實(shí)節(jié)省更多的是單字節(jié)寫(xiě)入時(shí)中間等待的時(shí)間。eeprom兩次寫(xiě)入間隔有一個(gè)時(shí)間要求,芯片手冊(cè)會(huì)給出這個(gè)數(shù)據(jù)的最大值,有的芯片是3ms,有的是5ms,有的10ms。這個(gè)時(shí)間表示芯片接收數(shù)據(jù)后,把數(shù)據(jù)存儲(chǔ)到內(nèi)部指定地址所需要的最大時(shí)間。eeprom芯片的頁(yè)寫(xiě)其實(shí)節(jié)省最大的是這個(gè)時(shí)間。

在這里插入圖片描述

圖2 某eeprom的頁(yè)寫(xiě)時(shí)序

??下圖是eeprom的單字節(jié)讀時(shí)序,因?yàn)榭梢宰x取任意存儲(chǔ)位置的數(shù)據(jù),所以在發(fā)送讀指令之前,需要告知存儲(chǔ)芯片本次讀取數(shù)據(jù)的存儲(chǔ)地址是多少。因此下圖讀時(shí)序中會(huì)先發(fā)送起始位、器件地址、寫(xiě)指示信號(hào)、從機(jī)應(yīng)答、寄存器地址、從機(jī)應(yīng)答。

??將需要讀取數(shù)據(jù)的存儲(chǔ)地址寫(xiě)入芯片之后,接下來(lái)就是讀取該地址的數(shù)據(jù)了。先發(fā)送重復(fù)起始位(不發(fā)送停止位的原因是在多主機(jī)系統(tǒng)中避免被別的主機(jī)搶占總線(xiàn)控制權(quán)),然后發(fā)送器件地址、讀指示位、從機(jī)應(yīng)答,之后從機(jī)將該寄存器的數(shù)據(jù)輸出到總線(xiàn)上,主機(jī)在時(shí)鐘高電平中部讀取數(shù)據(jù)總線(xiàn)上的數(shù)據(jù)即可,從機(jī)輸出一字節(jié)數(shù)據(jù)后主機(jī)不應(yīng)答從機(jī),最后主機(jī)發(fā)送停止位結(jié)束本次讀取操作。

在這里插入圖片描述

圖3 某eeprom的單字節(jié)讀時(shí)序

??下圖是eeprom的頁(yè)讀時(shí)序,與上圖的區(qū)別在于主機(jī)接收從機(jī)發(fā)送的第一字節(jié)數(shù)據(jù)后,主機(jī)把數(shù)據(jù)總線(xiàn)拉低,對(duì)從機(jī)做出應(yīng)答,從機(jī)就會(huì)輸出下一個(gè)存儲(chǔ)地址的數(shù)據(jù),從而實(shí)現(xiàn)連續(xù)地址的數(shù)據(jù)讀取。主機(jī)接收到指定個(gè)數(shù)的數(shù)據(jù)后,應(yīng)答時(shí)將數(shù)據(jù)總線(xiàn)拉高,不應(yīng)答從機(jī),然后發(fā)送停止位,結(jié)束本次讀取。

在這里插入圖片描述

圖4 某eeprom的頁(yè)讀時(shí)序

??最后在來(lái)查看一個(gè)IIC接口的溫濕度傳感器的讀時(shí)序,下圖時(shí)序中的指令數(shù)據(jù)其實(shí)與上述的存儲(chǔ)地址是一致的。下圖中包含2字節(jié)的命令,在發(fā)送寄存器地址時(shí)需要傳輸兩次,先傳輸高字節(jié)數(shù)據(jù)。后面寄存器的數(shù)據(jù)也是16位的,并且后面還包含一字節(jié)的CRC校驗(yàn)碼,所以讀取數(shù)據(jù)時(shí),需要連續(xù)讀取3字節(jié)數(shù)據(jù)。

??主機(jī)讀取前兩字節(jié)數(shù)據(jù)時(shí),也需要對(duì)從機(jī)做出應(yīng)答,在讀完3字節(jié)數(shù)據(jù)后,主機(jī)不再對(duì)從機(jī)做出應(yīng)答,然后發(fā)送停止位結(jié)束本次讀操作。

在這里插入圖片描述

圖5 某溫濕度傳感器IIC讀時(shí)序

??通過(guò)上面對(duì)幾個(gè)時(shí)序圖的分析可知,頁(yè)讀取(圖4)與從同一個(gè)寄存器讀取多個(gè)字節(jié)數(shù)據(jù)(圖5)的時(shí)序原理是一樣,就是讀前面字節(jié)數(shù)據(jù)后應(yīng)答從機(jī),最后一字節(jié)數(shù)據(jù)時(shí)不應(yīng)答從機(jī)。

??綜上,IIC的讀寫(xiě)時(shí)序中,器件地址的長(zhǎng)度一般是固定的,根據(jù)不同芯片設(shè)計(jì),寄存器地址的長(zhǎng)度不固定,讀寫(xiě)的數(shù)據(jù)長(zhǎng)度也是不固定的,所以在設(shè)計(jì)驅(qū)動(dòng)模塊時(shí),這兩部分需要根據(jù)實(shí)際情況自動(dòng)改變。

??最簡(jiǎn)單的想法就是通過(guò)一個(gè)計(jì)數(shù)器來(lái)對(duì)已經(jīng)發(fā)送的寄存器地址字節(jié)數(shù)和讀寫(xiě)數(shù)據(jù)的字節(jié)數(shù)計(jì)數(shù)。寫(xiě)入的寄存器地址字節(jié)數(shù)達(dá)到要求后在跳轉(zhuǎn)到別的狀態(tài),而讀寫(xiě)數(shù)據(jù)時(shí),只有讀寫(xiě)指定字節(jié)數(shù)數(shù)據(jù)時(shí),主機(jī)才能發(fā)送停止位。

??在fpga實(shí)現(xiàn)時(shí),寄存器的地址字節(jié)數(shù)、讀寫(xiě)數(shù)據(jù)字節(jié)數(shù)可以通過(guò)parameter常量進(jìn)行設(shè)置,便于使用時(shí)修改,且不會(huì)產(chǎn)生多余的電路。

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

??下表是該模塊的端口信號(hào),開(kāi)始讀寫(xiě)信號(hào)start必須在模塊空閑(rdy為高電平)時(shí)才能拉高,拉高一個(gè)時(shí)鐘周期即可。

表1 端口信號(hào)列表

信號(hào)位寬I/O含義
clk1I系統(tǒng)時(shí)鐘信號(hào),默認(rèn)100MHz
rst_n1I系統(tǒng)復(fù)位,默認(rèn)低電平有效;
start1I讀、寫(xiě)操作開(kāi)始信號(hào),高電平有效。
rw_flag1I讀、寫(xiě)指示信號(hào),高電平表示讀。
reg_addr可變I寄存器地址信號(hào)。
wdata可變I寫(xiě)入寄存器的數(shù)據(jù)。
rdata可變O從寄存器讀出的數(shù)據(jù)。
rdata_vld1O讀出數(shù)據(jù)有效指示信號(hào),高電平有效。
rdy1O模塊忙閑指示信號(hào),高電平表示模塊空閑。
scl1OIIC的串行時(shí)鐘線(xiàn)。
sda1IOIIC的雙向串行數(shù)據(jù)線(xiàn);

??本次設(shè)計(jì)采用一個(gè)狀態(tài)機(jī)嵌套三個(gè)計(jì)數(shù)器作為主體架構(gòu)實(shí)現(xiàn),狀態(tài)機(jī)包括7個(gè)狀態(tài)。狀態(tài)轉(zhuǎn)換圖如下所示,“將發(fā)送1字節(jié)加上應(yīng)答位劃分一個(gè)狀態(tài)”,這句話(huà)不完全狀態(tài)。

在這里插入圖片描述

圖6 狀態(tài)機(jī)狀態(tài)轉(zhuǎn)化圖

??將發(fā)送起始位、器件地址、寫(xiě)標(biāo)志位劃分為W_DEVICE_ADDR狀態(tài),發(fā)送讀數(shù)據(jù)劃分為WDATA狀態(tài)(這個(gè)狀態(tài)可能會(huì)讀取多個(gè)字節(jié)數(shù)據(jù),根據(jù)設(shè)置跳轉(zhuǎn)),發(fā)送讀、寫(xiě)寄存器地址劃分為W_REG_ADDR狀態(tài)(這個(gè)狀態(tài)依舊可能發(fā)送多個(gè)字節(jié)的數(shù)據(jù)),發(fā)送重復(fù)起始位、器件地址、讀標(biāo)志位劃分為R_DEVICE_ADDR狀態(tài),接收數(shù)據(jù)線(xiàn)的數(shù)據(jù)劃分為RDATA狀態(tài)(這個(gè)狀態(tài)依舊可能發(fā)送多個(gè)字節(jié)的數(shù)據(jù)),最后STOP狀態(tài)發(fā)送停止位。

??分頻計(jì)數(shù)器div_cnt在狀態(tài)機(jī)不處于空閑狀態(tài)時(shí),對(duì)系統(tǒng)時(shí)鐘進(jìn)行計(jì)數(shù),從而產(chǎn)生IIC時(shí)鐘信號(hào)scl,同時(shí)將scl的低電平、高電平的中間分別生成wr_flag和rd_flag標(biāo)志信號(hào),wr_flag位高電平表示可以對(duì)IIC數(shù)據(jù)線(xiàn)賦值,rd_flag高電平表示可以在此時(shí)讀取IIC數(shù)據(jù)線(xiàn)上的數(shù)據(jù)。

??計(jì)數(shù)器bit_cnt用于記錄每次讀寫(xiě)數(shù)據(jù)的位數(shù),當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)加1。狀態(tài)機(jī)在不同的狀態(tài),bit_cnt計(jì)數(shù)器的最大值不一樣,當(dāng)狀態(tài)機(jī)處于W_DEVICE_ADDR或R_DEVICE_ADDR狀態(tài)時(shí),需要發(fā)送起始位、器件地址、讀寫(xiě)標(biāo)志位、應(yīng)答位,所以bit_cnt計(jì)數(shù)器最大值為10-1,而狀態(tài)機(jī)位于WDATA,W_REG_ADDR,RDATA狀態(tài)時(shí),每次讀寫(xiě)的單位都是1字節(jié)數(shù)據(jù)、應(yīng)答位,所以bit_cnt計(jì)數(shù)器最大值為9-1。

??計(jì)數(shù)器byte_cnt用于記錄狀態(tài)機(jī)處于WDATA,W_REG_ADDR,RDATA狀態(tài)時(shí),接收或者發(fā)送的數(shù)據(jù)字節(jié)數(shù)。當(dāng)狀態(tài)機(jī)處于上述三個(gè)狀態(tài)且計(jì)數(shù)器bit_cnt計(jì)數(shù)結(jié)束時(shí)加1,根據(jù)需要讀寫(xiě)的寄存器地址字節(jié)數(shù)和讀寫(xiě)數(shù)據(jù)字節(jié)數(shù),確定該計(jì)數(shù)器在各個(gè)狀態(tài)下的最大值。

??狀態(tài)機(jī)的跳轉(zhuǎn)與三個(gè)計(jì)數(shù)器的結(jié)束條件有效,比較簡(jiǎn)單,此處不做過(guò)多介紹,看代碼即可。

??只需要注意一下下面幾個(gè)信號(hào)的變化即可,首先注意模塊有幾個(gè)parameter常量,包括系統(tǒng)時(shí)鐘的頻率、IIC時(shí)鐘的頻率、IIC的從機(jī)器件地址、讀寫(xiě)寄存器的字節(jié)數(shù)、讀寫(xiě)數(shù)據(jù)的字節(jié)數(shù),對(duì)應(yīng)代碼如下所示。

module iic_drive #(parameter           FCLK                    =   100_000_000         ,//系統(tǒng)時(shí)鐘頻率,默認(rèn)100MHz。parameter           FSCL                    =   400_000             ,//IIC時(shí)鐘頻率,默認(rèn)400KHz。parameter           REG_ADDR_BYTE_NUM       =   1                   ,//寄存器地址字節(jié)數(shù);parameter			DATA_BYTE_NUM           =   1		            ,//讀寫(xiě)數(shù)據(jù)字節(jié)數(shù)。parameter			DEVICE_ADDR             =   7'b1010000           //器件地址。
)(input									        clk		            ,//系統(tǒng)時(shí)鐘信號(hào);input									        rst_n	            ,//系統(tǒng)復(fù)位信號(hào),低電平有效;input                                           start               ,//開(kāi)始進(jìn)行讀寫(xiě)操作;input                                           rw_flag             ,//讀寫(xiě)標(biāo)志信號(hào),高電平表示讀操作,低電平表示寫(xiě)操作;input               [REG_ADDR_BYTE_NUM*8-1 : 0] reg_addr            ,//寄存器地址,讀寫(xiě)操作時(shí)共用的地址信號(hào);input               [DATA_BYTE_NUM*8 - 1 : 0]   wdata               ,//寫(xiě)數(shù)據(jù);output  reg         [DATA_BYTE_NUM*8 - 1 : 0]   rdata               ,//讀數(shù)據(jù)信號(hào);output  reg                                     rdata_vld           ,//讀數(shù)據(jù)輸出使能信號(hào),高電平有效;output  reg                                     rdy                 ,//模塊忙閑指示信號(hào),位高電平時(shí)可以接收上游模塊的讀寫(xiě)使能信號(hào);output  reg                                     scl                 ,//IIC的時(shí)鐘信號(hào);inout                                           sda                 ,//IIC的雙向數(shù)據(jù)信號(hào);output  reg                                     ack_flag             //高電平表示應(yīng)答失敗;
);

??當(dāng)接收到上游模塊的讀寫(xiě)開(kāi)始信號(hào)(start為高電平)時(shí),將寄存器地址、寫(xiě)數(shù)據(jù)、讀寫(xiě)狀態(tài)信號(hào)暫存,便于后續(xù)讀寫(xiě)過(guò)程中使用,讀寫(xiě)寄存器的地址和數(shù)據(jù)信號(hào)全部采用參數(shù)化設(shè)計(jì),不需要人為修改信號(hào)位寬。將器件地址和起始位、寫(xiě)指示位拼接,便于后續(xù)使用。對(duì)應(yīng)代碼如下所示:

    //暫存器件地址和起始位還有寫(xiě)指示位。assign device_addr = {1'b0,DEVICE_ADDR,1'b0};//開(kāi)始信號(hào)有效時(shí),把待發(fā)送的信號(hào)暫存。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;wdata_r <= 0;rw_flag_r <= 1'b0;reg_addr_r <= 0;endelse if(start)beginwdata_r <= wdata;rw_flag_r <= rw_flag;reg_addr_r <= reg_addr;endend

??下面就是狀態(tài)機(jī)的跳轉(zhuǎn)了,狀態(tài)機(jī)采用三段式,下面代碼包含其次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換以及次態(tài)變化最重要的兩段,跳轉(zhuǎn)很簡(jiǎn)單,也有注釋,基本上就是對(duì)應(yīng)數(shù)據(jù)讀寫(xiě)完畢后就跳轉(zhuǎn),不做多余講解。

    //狀態(tài)機(jī),次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換;always@(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE;endelse beginstate_c <= state_n;endend//狀態(tài)機(jī)次態(tài)的跳轉(zhuǎn);always@(*)begincase(state_c)IDLE : beginif(start)begin//開(kāi)始信號(hào)有效時(shí),跳轉(zhuǎn)到發(fā)送起始位和器件地址的狀態(tài);state_n = W_DEVICE_ADDR;endelse beginstate_n = state_c;endendW_DEVICE_ADDR : beginif(end_bit_cnt)begin//器件地址發(fā)送完成后,跳轉(zhuǎn)到寫(xiě)寄存器地址狀態(tài);state_n = W_REG_ADDR;endelse beginstate_n = state_c;endendW_REG_ADDR : beginif(end_byte_cnt)begin//寄存器地址寫(xiě)入完成后,if(rw_flag_r)//如果是讀操作,則跳轉(zhuǎn)到重復(fù)起始位和寫(xiě)器件地址狀態(tài);state_n = R_DEVICE_ADDR;else//如果是寫(xiě)操作,跳轉(zhuǎn)到寫(xiě)數(shù)據(jù)狀態(tài);state_n = WDATA;endelse beginstate_n = state_c;endendWDATA : beginif(end_byte_cnt)begin//如果數(shù)據(jù)全部寫(xiě)入完成,則跳轉(zhuǎn)到停止?fàn)顟B(tài);state_n = STOP;endelse beginstate_n = state_c;endendR_DEVICE_ADDR : beginif(end_bit_cnt)begin//如果重復(fù)起始位、器件地址、讀指示位寫(xiě)入完畢,則跳轉(zhuǎn)到讀數(shù)據(jù)狀態(tài);state_n = RDATA;endelse beginstate_n = state_c;endendRDATA : beginif(end_byte_cnt)begin//讀出一次需要讀出的所有數(shù)據(jù)后,跳轉(zhuǎn)到停止?fàn)顟B(tài);state_n = STOP;endelse beginstate_n = state_c;endendSTOP : beginif(end_div_cnt)begin//停止位發(fā)送完畢后,跳轉(zhuǎn)到空閑狀態(tài);state_n = IDLE;endelse beginstate_n = state_c;endenddefault:begin//state_n = IDLE;endendcaseend

??然后就是分頻計(jì)數(shù)器div_cnt,當(dāng)狀態(tài)機(jī)不處于空閑狀態(tài)時(shí),對(duì)系統(tǒng)時(shí)鐘進(jìn)行計(jì)數(shù),從而生成scl時(shí)鐘信號(hào),對(duì)應(yīng)代碼如下所示。生成該計(jì)數(shù)器相關(guān)的四個(gè)信號(hào),計(jì)數(shù)器計(jì)數(shù)到一半時(shí),需要把IIC時(shí)鐘線(xiàn)scl拉低,所以生成標(biāo)志信號(hào)l2h_flag表示scl下降沿。同理生成h2l_flag表示scl上升沿,主機(jī)在scl低電平中間驅(qū)動(dòng)數(shù)據(jù)線(xiàn)SDA,所以分頻計(jì)數(shù)器計(jì)數(shù)到1/4時(shí),把wr_flag拉高,表示主機(jī)可以寫(xiě)入數(shù)據(jù)。主機(jī)在高電平中間讀取SDA數(shù)據(jù),所以分頻計(jì)數(shù)器計(jì)數(shù)到3/4時(shí),把rd_flag拉高,表示主機(jī)可以讀取從機(jī)的數(shù)據(jù)。

    //分頻計(jì)數(shù)器,用于生成SCL信號(hào)。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//div_cnt <= 0;endelse if(state_c != IDLE)beginif(end_div_cnt)//狀態(tài)機(jī)不處于空閑狀態(tài)時(shí),對(duì)系統(tǒng)時(shí)鐘進(jìn)行計(jì)數(shù);div_cnt <= 0;elsediv_cnt <= div_cnt + 1;endend//根據(jù)clk_cnt生成各種標(biāo)志信號(hào),由于計(jì)數(shù)器從零開(kāi)始計(jì)數(shù),并且下面為時(shí)序電路,所以產(chǎn)生條件是為對(duì)應(yīng)值減2。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;l2h_flag <= 1'b0;h2l_flag <= 1'b0;wr_flag  <= 1'b0;rd_flag  <= 1'b0;end_div_cnt <= 1'b0;endelse beginl2h_flag <= (div_cnt == CLK_DIV / 2);//在計(jì)數(shù)器div_cnt計(jì)數(shù)到一半時(shí)scl拉高;h2l_flag <= (div_cnt == 0);//在計(jì)數(shù)器div_cnt計(jì)數(shù)0時(shí)scl拉低;end_div_cnt <= (div_cnt == CLK_DIV - 2);//在計(jì)數(shù)器div_cnt計(jì)數(shù)結(jié)束時(shí)scl拉低;wr_flag <= (div_cnt == CLK_DIV / 4);//在計(jì)數(shù)器div_cnt計(jì)數(shù)四分之一處SDA寫(xiě)入數(shù)據(jù);rd_flag <= (div_cnt == CLK_DIV*3 / 4);//在計(jì)數(shù)器div_cnt計(jì)數(shù)四分之三處從SDA讀取數(shù)據(jù);endend

??接下來(lái)是用來(lái)記錄發(fā)送字節(jié)數(shù)的計(jì)數(shù)器bit_cnt,對(duì)應(yīng)代碼如下所示:當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)該計(jì)數(shù)器加1,表示經(jīng)過(guò)了發(fā)送1位數(shù)據(jù)的時(shí)間。根據(jù)狀態(tài)機(jī)所處狀態(tài)不同,每次需要發(fā)送或者讀取的數(shù)據(jù)位數(shù)不同,使用bit_cnt_num去控制該計(jì)數(shù)器在狀態(tài)機(jī)不同狀態(tài)的最大值。狀態(tài)機(jī)在W_DEVICE_ADDR和R_DEVICE_ADDR需要發(fā)送起始位、7位器件地址、1位讀寫(xiě)指示位、1位應(yīng)答位,需要持續(xù)10個(gè)時(shí)鐘周期,而寫(xiě)寄存器地址、讀寫(xiě)數(shù)據(jù)狀態(tài)都是8位數(shù)據(jù)加1位應(yīng)答位,所以最大值為9。特別注意該計(jì)數(shù)器在狀態(tài)機(jī)處于空閑狀態(tài)時(shí)需要清零。

    //數(shù)據(jù)位計(jì)數(shù)器bit_cnt,初始值為0,當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束的時(shí)候加一。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;bit_cnt <= 0;endelse if(state_c == IDLE)begin//狀態(tài)機(jī)處于空閑狀態(tài)時(shí)清零;bit_cnt <= 0;endelse if(add_bit_cnt)beginif(end_bit_cnt)bit_cnt <= 0;elsebit_cnt <= bit_cnt + 1;endendassign add_bit_cnt = end_div_cnt;//計(jì)數(shù)器加一條件,當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)有效;assign end_bit_cnt = add_bit_cnt && (bit_cnt == bit_cnt_num - 1);//用于表示每個(gè)狀態(tài)每次發(fā)送的數(shù)據(jù)位數(shù),發(fā)送器件地址之前需要發(fā)送起始位,在加上應(yīng)答位,需要是個(gè)SCL時(shí)鐘。//其余狀態(tài)每次發(fā)送一字節(jié)數(shù)據(jù)后需要發(fā)送應(yīng)答位,所以計(jì)數(shù)器最大值為9。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;bit_cnt_num <= 4'd9;end//寫(xiě)器件地址和起始位、讀寫(xiě)指示位,總共是10位數(shù)據(jù),所以計(jì)數(shù)器的最大值為10-1;else if((state_c == W_DEVICE_ADDR) || (state_c == R_DEVICE_ADDR))beginbit_cnt_num <= 4'd10;endelse begin//其余狀態(tài)下計(jì)數(shù)器最大值為9。bit_cnt_num <= 4'd9;endend

??然后是用來(lái)記錄狀態(tài)機(jī)在寫(xiě)寄存器地址、讀寫(xiě)數(shù)據(jù)階段讀寫(xiě)數(shù)據(jù)字節(jié)數(shù)的計(jì)數(shù)器byte_cnt,對(duì)應(yīng)代碼如下圖所示。當(dāng)狀態(tài)機(jī)處于這幾個(gè)狀態(tài)下,計(jì)數(shù)器bit_cnt計(jì)數(shù)結(jié)束時(shí)加1,讀寫(xiě)數(shù)據(jù)的最大值在狀態(tài)機(jī)不同狀態(tài)頁(yè)不相同,與前文設(shè)置的parameter參數(shù)有關(guān),通過(guò)byte_cnt_num信號(hào)的值控制計(jì)數(shù)器byte_cnt的最大值。

    //發(fā)送字節(jié)數(shù)的計(jì)數(shù)器,用于計(jì)數(shù)發(fā)送數(shù)據(jù)的字節(jié)數(shù)據(jù)。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0。byte_cnt <= 0;endelse if(state_c == IDLE)begin//狀態(tài)機(jī)處于空閑狀態(tài)時(shí)清零;byte_cnt <= 0;endelse if(add_byte_cnt)beginif(end_byte_cnt)byte_cnt <= 0;elsebyte_cnt <= byte_cnt + 1;endend//當(dāng)狀態(tài)機(jī)處于寫(xiě)寄存器地址或?qū)憯?shù)據(jù)或讀數(shù)據(jù)狀態(tài)且發(fā)送數(shù)據(jù)位計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)加1。assign add_byte_cnt = ((state_c == W_REG_ADDR) || (state_c == WDATA) || (state_c == RDATA)) && end_bit_cnt;assign end_byte_cnt = add_byte_cnt && (byte_cnt == byte_cnt_num);//當(dāng)計(jì)數(shù)到指定數(shù)值時(shí)清零。//字節(jié)計(jì)數(shù)器的最大值,初始值為寫(xiě)寄存器地址的長(zhǎng)度;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;byte_cnt_num <= REG_ADDR_BYTE_NUM - 1;endelse if(state_c == W_REG_ADDR)beginbyte_cnt_num <= REG_ADDR_BYTE_NUM - 1;endelse if((state_c == WDATA) || (state_c == RDATA))beginbyte_cnt_num <= DATA_BYTE_NUM - 1;endend

??前文就將狀態(tài)機(jī)和三個(gè)計(jì)數(shù)器的主體架構(gòu)搭建好了,后面就根據(jù)這個(gè)架構(gòu)去生成本文需要的輸出信號(hào)了,是不是頁(yè)很簡(jiǎn)單。

??首先生成IIC的時(shí)鐘信號(hào)scl,當(dāng)狀態(tài)機(jī)不處于空閑狀態(tài)且l2h_flag有效時(shí)拉高。在產(chǎn)生起始位時(shí),時(shí)鐘信號(hào)需要保持一段時(shí)間高電平,狀態(tài)機(jī)在W_REG_ADDR狀態(tài)下,發(fā)送第一位數(shù)據(jù)時(shí),時(shí)鐘信號(hào)需要一直保持高電平,否則只要h2l_flag有效,就把scl拉低,對(duì)應(yīng)代碼如下所示。

    //生成串行時(shí)鐘信號(hào);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;scl <= 1'b1;end//當(dāng)拉高條件有效或者狀態(tài)機(jī)處于空閑狀態(tài)時(shí)拉高。else if(l2h_flag || state_c == IDLE)beginscl <= 1'b1;end//只有在初始發(fā)送起始位時(shí)滿(mǎn)足拉低條件時(shí)不拉低,其余情況下滿(mǎn)足條件均要拉低;else if((((state_c == W_DEVICE_ADDR) && bit_cnt > 0) || (state_c != W_DEVICE_ADDR)) && h2l_flag)beginscl <= 1'b0;endend

??然后生成串行數(shù)據(jù)輸出信號(hào)sda_out,初始時(shí)該信號(hào)為高電平,狀態(tài)機(jī)在不同狀態(tài)輸出不同數(shù)據(jù)即可。狀態(tài)機(jī)處于R_REG_ADDR、RDATA、STOP需要特別注意,重復(fù)起始位的產(chǎn)生需要在寫(xiě)數(shù)據(jù)(bit_cnt==0 && wr_flag)時(shí)拉高,然在scl的高電平中間(bit_cnt==0 && rd_flag)拉低。讀指示位(bit_cnt == bit_cnt_num-2 && wr_flag)需要輸出高電平。

??讀數(shù)據(jù)階段主機(jī)需要在讀取完最后一字節(jié)數(shù)據(jù)后輸出高電平,表示不應(yīng)答從機(jī),如果讀取的數(shù)據(jù)不是最后一字節(jié)數(shù)據(jù),則輸出低電平應(yīng)答從機(jī),繼續(xù)接收從機(jī)輸出的數(shù)據(jù)。

??然后在發(fā)送停止位時(shí)需要先在scl為低電平時(shí)把sda拉低,在scl為高電平時(shí)拉高sda,從而表示出停止位。

    //賦值輸出信號(hào);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;sda_out <= 1'b1;endelse begincase (state_c)W_DEVICE_ADDR : beginif((~bit_cnt[3]) && wr_flag)//輸出器件地址和寫(xiě)指示位;sda_out <= device_addr[8 - bit_cnt];endW_REG_ADDR : beginif((~bit_cnt[3]) && wr_flag)//輸出需要寫(xiě)入的寄存器地址;sda_out <= reg_addr_r[REG_ADDR_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt];//reg_addr_r[7 - bit_cnt];endWDATA : begin//輸出寫(xiě)數(shù)據(jù),先輸出高字節(jié)數(shù)據(jù);if((~bit_cnt[3]) && wr_flag)sda_out <= wdata_r[DATA_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt];endR_DEVICE_ADDR : begin//輸出重復(fù)開(kāi)始信號(hào),器件地址和讀指示位;if(wr_flag)//當(dāng)SCL低電平時(shí)把SDA拉低,便于后續(xù)產(chǎn)生起始位;if(bit_cnt == 0 || bit_cnt == bit_cnt_num - 2)sda_out <= 1'b1;else//產(chǎn)生起始位之后,在SCL低電平中間發(fā)送器件地址;sda_out <= device_addr[8 - bit_cnt];else if(rd_flag && bit_cnt == 0)//在SCL高電平的時(shí)候拉低SDA,發(fā)送重復(fù)起始位;sda_out <= 1'b0;endRDATA : beginif(bit_cnt == bit_cnt_num - 1 && wr_flag)if(byte_cnt == DATA_BYTE_NUM - 1)//如果是讀取的最后一字節(jié)數(shù)據(jù),則不應(yīng)答。sda_out <= 1'b1;else//如果不是最后一字節(jié)數(shù)據(jù),則進(jìn)行應(yīng)答。sda_out <= 1'b0;endSTOP : beginif(wr_flag)//停止信號(hào)需要先拉低;sda_out <= 1'b0;else if(rd_flag)//在SCL高電平的時(shí)候拉高,表示停止位;sda_out <= 1'b1;enddefault : sda_out <= sda_out;endcaseendend

??上述生成了串行數(shù)據(jù)的輸出信號(hào),接下來(lái)就需要生成三態(tài)門(mén)使能信號(hào)sda_out_en,把上面生成的數(shù)據(jù)輸出。如果使用iobufr則可以省略該信號(hào)。因?yàn)橐话阆到y(tǒng)中只會(huì)存在一個(gè)主機(jī),所以主機(jī)除了需要從機(jī)做出應(yīng)答的狀態(tài),其余時(shí)間主機(jī)全程驅(qū)動(dòng)數(shù)據(jù)線(xiàn)。

    //賦值輸出使能信號(hào),除了從機(jī)應(yīng)答之外,其余全為高電平;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為1;sda_out_en <= 1'b1;endelse if(wr_flag)begincase (state_c)//在寫(xiě)器件地址、寫(xiě)寄存器地址、寫(xiě)數(shù)據(jù)、讀過(guò)程的寫(xiě)器件地址的從機(jī)應(yīng)答狀態(tài),都需要釋放總線(xiàn);W_DEVICE_ADDR,WDATA,R_DEVICE_ADDR,W_REG_ADDR : beginif(bit_cnt == 0)//當(dāng)計(jì)數(shù)器為0時(shí),總線(xiàn)拉高,開(kāi)始寫(xiě)入下一字節(jié)數(shù)據(jù);sda_out_en <= 1'b1;else if(bit_cnt == bit_cnt_num - 1)//當(dāng)寫(xiě)入最后一位數(shù)據(jù)后,將使能信號(hào)拉低,釋放總線(xiàn);sda_out_en <= 1'b0;endSTOP : beginif(bit_cnt == 0)//當(dāng)計(jì)數(shù)器為0時(shí),總線(xiàn)拉高,開(kāi)始寫(xiě)入下一字節(jié)數(shù)據(jù);sda_out_en <= 1'b1;endRDATA : begin//在讀數(shù)據(jù)階段,主機(jī)應(yīng)答時(shí)需要控制總線(xiàn),其余時(shí)間釋放總線(xiàn);if(bit_cnt == 0)sda_out_en <= 1'b0;else if(bit_cnt == bit_cnt_num - 1)sda_out_en <= 1'b1;enddefault: ;endcaseendend

??因此使能信號(hào)初始為高電平,狀態(tài)機(jī)處于W_DEVICE_ADDR、WDATA、R_DEVICE_ADDR、W_REG_ADDR、STOP狀態(tài)時(shí),在從機(jī)應(yīng)答的時(shí)候釋放總線(xiàn)。而讀數(shù)據(jù)狀態(tài)RDATA只有在應(yīng)答時(shí)主機(jī)才控制總線(xiàn),所以與其他狀態(tài)的控制狀態(tài)剛好相反。STOP狀態(tài)下bit_cnt==bit_cnt_num-1不會(huì)滿(mǎn)足,所以使能在該狀態(tài)下不會(huì)被拉低。

??然后就是主機(jī)接收從機(jī)的數(shù)據(jù)了,如下所示,為了該用戶(hù)接口的信號(hào)保持穩(wěn)定,則將接收完成的數(shù)據(jù)打一拍后輸出,在SCL的中部接收數(shù)據(jù),先接收的數(shù)據(jù)位于高字節(jié)的高位。

    //在讀數(shù)據(jù)階段,讀取總線(xiàn)上的數(shù)據(jù);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;rdata_r <= 0;end//當(dāng)處于讀數(shù)據(jù)階段時(shí),在SCL高電平中間讀取數(shù)據(jù)總線(xiàn)上的數(shù)據(jù);else if(state_c == RDATA && rd_flag)beginrdata_r[DATA_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt] <= sda_in;endend//數(shù)據(jù)輸出有效指示信號(hào),該信號(hào)為高電平時(shí),表示讀取的數(shù)據(jù)rdata有效;always@(posedge clk)beginrdata_vld_r <= (state_c == RDATA) && rd_flag && (bit_cnt == bit_cnt_num - 2) && (byte_cnt == byte_cnt_num);end//將讀取的數(shù)據(jù)輸出。always@(posedge clk)beginrdata <= rdata_vld_r ? rdata_r : rdata;rdata_vld <= rdata_vld_r;end

??最后就是模塊忙閑指示信號(hào)和應(yīng)答失敗的指示信號(hào),模塊接收到開(kāi)始信號(hào)或狀態(tài)機(jī)不處于空閑狀態(tài)時(shí),模塊處于忙碌狀態(tài),rdy拉低,其余時(shí)間拉高,表示模塊空閑,注意該信號(hào)只能采用組合邏輯生成。

??最后應(yīng)答失敗指示信號(hào),在各個(gè)狀態(tài)的應(yīng)答位中部讀取串行數(shù)據(jù)線(xiàn)sda的狀態(tài),低電平表示應(yīng)答,高電平表示從機(jī)不應(yīng)答。

    //模塊忙閑指示信號(hào),當(dāng)模塊接收到開(kāi)始信號(hào)或者狀態(tài)機(jī)不處于空閑狀態(tài)時(shí)拉低,表示模塊處于工作狀態(tài);always@(*)beginif(start || (state_c != IDLE))rdy = 1'b0;else//其余時(shí)間拉高,表示模塊處于空閑狀態(tài),上游模塊可以發(fā)起寫(xiě)或者讀操作;rdy = 1'b1;end//從機(jī)應(yīng)答失敗標(biāo)志信號(hào),高電平表示應(yīng)答失敗,每次開(kāi)始讀寫(xiě)操作時(shí)清零;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;ack_flag <= 1'b0;endelse if(start)begin//接收到開(kāi)始讀寫(xiě)請(qǐng)求信號(hào)時(shí)拉低;ack_flag <= 1'b0;end//在從機(jī)應(yīng)答狀態(tài)下,將接收到的應(yīng)答信號(hào)輸出,高電平表示應(yīng)答失敗,低電平表示應(yīng)答成功。else if(((state_c == WDATA) || (state_c == W_DEVICE_ADDR) || (state_c == W_REG_ADDR) || (state_c == R_DEVICE_ADDR)) && rd_flag && (bit_cnt == bit_cnt_num - 1))beginack_flag <= sda_in;endend

4、參考代碼

??上述就是該模塊的設(shè)計(jì),是不是也很簡(jiǎn)單?總共加注釋也就402行代碼,沒(méi)有采用任何縮寫(xiě),后文通過(guò)驅(qū)動(dòng)eeprom對(duì)該模塊的設(shè)計(jì)進(jìn)行仿真和驗(yàn)證,最后可以看綜合結(jié)果,消耗的資源也是比較少的,寄存器地址和讀寫(xiě)數(shù)據(jù)均為1字節(jié)時(shí),也只需要消耗八十多個(gè)LUT和觸發(fā)器資源。

??該模塊的完整參考代碼如下所示:

module iic_drive #(parameter           FCLK                    =   100_000_000         ,//系統(tǒng)時(shí)鐘頻率,默認(rèn)100MHz。parameter           FSCL                    =   400_000             ,//IIC時(shí)鐘頻率,默認(rèn)400KHz。parameter           REG_ADDR_BYTE_NUM       =   1                   ,//寄存器地址字節(jié)數(shù);parameter			DATA_BYTE_NUM           =   1		            ,//讀寫(xiě)數(shù)據(jù)字節(jié)數(shù)。parameter			DEVICE_ADDR             =   7'b1010000           //器件地址。
)(input									        clk		            ,//系統(tǒng)時(shí)鐘信號(hào);input									        rst_n	            ,//系統(tǒng)復(fù)位信號(hào),低電平有效;input                                           start               ,//開(kāi)始進(jìn)行讀寫(xiě)操作;input                                           rw_flag             ,//讀寫(xiě)標(biāo)志信號(hào),高電平表示讀操作,低電平表示寫(xiě)操作;input               [REG_ADDR_BYTE_NUM*8-1 : 0] reg_addr            ,//寄存器地址,讀寫(xiě)操作時(shí)共用的地址信號(hào);input               [DATA_BYTE_NUM*8 - 1 : 0]   wdata               ,//寫(xiě)數(shù)據(jù);output  reg         [DATA_BYTE_NUM*8 - 1 : 0]   rdata               ,//讀數(shù)據(jù)信號(hào);output  reg                                     rdata_vld           ,//讀數(shù)據(jù)輸出使能信號(hào),高電平有效;output  reg                                     rdy                 ,//模塊忙閑指示信號(hào),位高電平時(shí)可以接收上游模塊的讀寫(xiě)使能信號(hào);output  reg                                     scl                 ,//IIC的時(shí)鐘信號(hào);inout                                           sda                 ,//IIC的雙向數(shù)據(jù)信號(hào);output  reg                                     ack_flag             //高電平表示應(yīng)答失敗;
);localparam          CLK_DIV             =       FCLK / FSCL         ;//計(jì)算計(jì)數(shù)器div_cnt的結(jié)束值;localparam          CLK_DIV_W           =       clogb2(CLK_DIV - 1) ;//計(jì)算計(jì)數(shù)器div_cnt的位寬;//根據(jù)比較寄存器地址和讀寫(xiě)數(shù)據(jù)的大小,然后自動(dòng)計(jì)算處byte_cnt計(jì)數(shù)器位寬。localparam          BYTE_CNT_W          =       (REG_ADDR_BYTE_NUM  > DATA_BYTE_NUM) ? clogb2(REG_ADDR_BYTE_NUM-1) : clogb2(DATA_BYTE_NUM-1);//Four-stage state machine;localparam          IDLE                =       7'b0000001          ;//狀態(tài)機(jī)空閑狀態(tài);localparam          W_DEVICE_ADDR       =       7'b0000010          ;//狀態(tài)機(jī)寫(xiě)器件地址狀態(tài);localparam          W_REG_ADDR          =       7'b0000100          ;//狀態(tài)機(jī)寫(xiě)寄存器地址狀態(tài);localparam          WDATA               =       7'b0001000          ;//狀態(tài)機(jī)寫(xiě)數(shù)據(jù)狀態(tài);localparam          R_DEVICE_ADDR       =       7'b0010000          ;//狀態(tài)機(jī)發(fā)送讀器件地址狀態(tài);localparam          RDATA	            =       7'b0100000          ;//狀態(tài)機(jī)讀數(shù)據(jù)狀態(tài);localparam          STOP	            =       7'b1000000          ;//狀態(tài)機(jī)停止?fàn)顟B(tài);reg                                             l2h_flag            ;reg                                             h2l_flag            ;reg                                             wr_flag             ;reg                                             rd_flag             ;reg                                             end_div_cnt         ;reg                                             rw_flag_r           ;//reg                                             sda_out             ;reg                                             sda_out_en          ;reg                 [6 : 0]	                    state_n             ;reg                 [6 : 0]	                    state_c             ;reg                 [3 : 0] 	                bit_cnt             ;//reg                 [3 : 0]                     bit_cnt_num         ;//reg                 [CLK_DIV_W - 1 : 0] 	    div_cnt             ;//reg                 [BYTE_CNT_W - 1 : 0] 	    byte_cnt            ;//reg                 [BYTE_CNT_W - 1 : 0]        byte_cnt_num        ;//reg                 [DATA_BYTE_NUM*8 - 1 : 0]   wdata_r             ;reg                 [REG_ADDR_BYTE_NUM*8 - 1 : 0] reg_addr_r        ;reg                 [DATA_BYTE_NUM*8 - 1 : 0]   rdata_r             ;reg                                             rdata_vld_r         ;wire                     		                add_byte_cnt        ;wire                     		                end_byte_cnt        ;wire                [8 : 0]                     device_addr         ;wire                                            sda_in              ;wire       		                                add_bit_cnt         ;wire       		                                end_bit_cnt         ;// Pullup output (connect directly to top-level port)//PULLUP PULLUP_inst (.O(sda));//雙向IO控制;assign sda_in = sda;assign sda = sda_out_en ? sda_out : 1'bz;//自動(dòng)計(jì)算位寬函數(shù);function integer clogb2(input integer depth);beginif(depth == 0)clogb2 = 1;else if(depth != 0)for(clogb2=0 ; depth>0 ; clogb2=clogb2+1)depth=depth >> 1;endendfunction//暫存器件地址和起始位還有寫(xiě)指示位。assign device_addr = {1'b0,DEVICE_ADDR,1'b0};//開(kāi)始信號(hào)有效時(shí),把待發(fā)送的信號(hào)暫存。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;wdata_r <= 0;rw_flag_r <= 1'b0;reg_addr_r <= 0;endelse if(start)beginwdata_r <= wdata;rw_flag_r <= rw_flag;reg_addr_r <= reg_addr;endend//狀態(tài)機(jī),次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換;always@(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE;endelse beginstate_c <= state_n;endend//狀態(tài)機(jī)次態(tài)的跳轉(zhuǎn);always@(*)begincase(state_c)IDLE : beginif(start)begin//開(kāi)始信號(hào)有效時(shí),跳轉(zhuǎn)到發(fā)送起始位和器件地址的狀態(tài);state_n = W_DEVICE_ADDR;endelse beginstate_n = state_c;endendW_DEVICE_ADDR : beginif(end_bit_cnt)begin//器件地址發(fā)送完成后,跳轉(zhuǎn)到寫(xiě)寄存器地址狀態(tài);state_n = W_REG_ADDR;endelse beginstate_n = state_c;endendW_REG_ADDR : beginif(end_byte_cnt)begin//寄存器地址寫(xiě)入完成后,if(rw_flag_r)//如果是讀操作,則跳轉(zhuǎn)到重復(fù)起始位和寫(xiě)器件地址狀態(tài);state_n = R_DEVICE_ADDR;else//如果是寫(xiě)操作,跳轉(zhuǎn)到寫(xiě)數(shù)據(jù)狀態(tài);state_n = WDATA;endelse beginstate_n = state_c;endendWDATA : beginif(end_byte_cnt)begin//如果數(shù)據(jù)全部寫(xiě)入完成,則跳轉(zhuǎn)到停止?fàn)顟B(tài);state_n = STOP;endelse beginstate_n = state_c;endendR_DEVICE_ADDR : beginif(end_bit_cnt)begin//如果重復(fù)起始位、器件地址、讀指示位寫(xiě)入完畢,則跳轉(zhuǎn)到讀數(shù)據(jù)狀態(tài);state_n = RDATA;endelse beginstate_n = state_c;endendRDATA : beginif(end_byte_cnt)begin//讀出一次需要讀出的所有數(shù)據(jù)后,跳轉(zhuǎn)到停止?fàn)顟B(tài);state_n = STOP;endelse beginstate_n = state_c;endendSTOP : beginif(end_div_cnt)begin//停止位發(fā)送完畢后,跳轉(zhuǎn)到空閑狀態(tài);state_n = IDLE;endelse beginstate_n = state_c;endenddefault:begin//state_n = IDLE;endendcaseend//分頻計(jì)數(shù)器,用于生成SCL信號(hào)。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//div_cnt <= 0;endelse if(state_c != IDLE)beginif(end_div_cnt)//狀態(tài)機(jī)不處于空閑狀態(tài)時(shí),對(duì)系統(tǒng)時(shí)鐘進(jìn)行計(jì)數(shù);div_cnt <= 0;elsediv_cnt <= div_cnt + 1;endend//根據(jù)clk_cnt生成各種標(biāo)志信號(hào),由于計(jì)數(shù)器從零開(kāi)始計(jì)數(shù),并且下面為時(shí)序電路,所以產(chǎn)生條件是為對(duì)應(yīng)值減2。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;l2h_flag <= 1'b0;h2l_flag <= 1'b0;wr_flag  <= 1'b0;rd_flag  <= 1'b0;end_div_cnt <= 1'b0;endelse beginl2h_flag <= (div_cnt == CLK_DIV / 2);//在計(jì)數(shù)器div_cnt計(jì)數(shù)到一半時(shí)scl拉高;h2l_flag <= (div_cnt == 0);//在計(jì)數(shù)器div_cnt計(jì)數(shù)0時(shí)scl拉低;end_div_cnt <= (div_cnt == CLK_DIV - 2);//在計(jì)數(shù)器div_cnt計(jì)數(shù)結(jié)束時(shí)scl拉低;wr_flag <= (div_cnt == CLK_DIV / 4);//在計(jì)數(shù)器div_cnt計(jì)數(shù)四分之一處SDA寫(xiě)入數(shù)據(jù);rd_flag <= (div_cnt == CLK_DIV*3 / 4);//在計(jì)數(shù)器div_cnt計(jì)數(shù)四分之三處從SDA讀取數(shù)據(jù);endend//數(shù)據(jù)位計(jì)數(shù)器bit_cnt,初始值為0,當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束的時(shí)候加一。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;bit_cnt <= 0;endelse if(state_c == IDLE)begin//狀態(tài)機(jī)處于空閑狀態(tài)時(shí)清零;bit_cnt <= 0;endelse if(add_bit_cnt)beginif(end_bit_cnt)bit_cnt <= 0;elsebit_cnt <= bit_cnt + 1;endendassign add_bit_cnt = end_div_cnt;//計(jì)數(shù)器加一條件,當(dāng)分頻計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)有效;assign end_bit_cnt = add_bit_cnt && (bit_cnt == bit_cnt_num - 1);//用于表示每個(gè)狀態(tài)每次發(fā)送的數(shù)據(jù)位數(shù),發(fā)送器件地址之前需要發(fā)送起始位,在加上應(yīng)答位,需要是個(gè)SCL時(shí)鐘。//其余狀態(tài)每次發(fā)送一字節(jié)數(shù)據(jù)后需要發(fā)送應(yīng)答位,所以計(jì)數(shù)器最大值為9。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;bit_cnt_num <= 4'd9;end//寫(xiě)器件地址和起始位、讀寫(xiě)指示位,總共是10位數(shù)據(jù),所以計(jì)數(shù)器的最大值為10-1;else if((state_c == W_DEVICE_ADDR) || (state_c == R_DEVICE_ADDR))beginbit_cnt_num <= 4'd10;endelse begin//其余狀態(tài)下計(jì)數(shù)器最大值為9。bit_cnt_num <= 4'd9;endend//發(fā)送字節(jié)數(shù)的計(jì)數(shù)器,用于計(jì)數(shù)發(fā)送數(shù)據(jù)的字節(jié)數(shù)據(jù)。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0。byte_cnt <= 0;endelse if(state_c == IDLE)begin//狀態(tài)機(jī)處于空閑狀態(tài)時(shí)清零;byte_cnt <= 0;endelse if(add_byte_cnt)beginif(end_byte_cnt)byte_cnt <= 0;elsebyte_cnt <= byte_cnt + 1;endend//當(dāng)狀態(tài)機(jī)處于寫(xiě)寄存器地址或?qū)憯?shù)據(jù)或讀數(shù)據(jù)狀態(tài)且發(fā)送數(shù)據(jù)位計(jì)數(shù)器計(jì)數(shù)結(jié)束時(shí)加1。assign add_byte_cnt = ((state_c == W_REG_ADDR) || (state_c == WDATA) || (state_c == RDATA)) && end_bit_cnt;assign end_byte_cnt = add_byte_cnt && (byte_cnt == byte_cnt_num);//當(dāng)計(jì)數(shù)到指定數(shù)值時(shí)清零。//字節(jié)計(jì)數(shù)器的最大值,初始值為寫(xiě)寄存器地址的長(zhǎng)度;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;byte_cnt_num <= REG_ADDR_BYTE_NUM - 1;endelse if(state_c == W_REG_ADDR)beginbyte_cnt_num <= REG_ADDR_BYTE_NUM - 1;endelse if((state_c == WDATA) || (state_c == RDATA))beginbyte_cnt_num <= DATA_BYTE_NUM - 1;endend//生成串行時(shí)鐘信號(hào);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;scl <= 1'b1;end//當(dāng)拉高條件有效或者狀態(tài)機(jī)處于空閑狀態(tài)時(shí)拉高。else if(l2h_flag || state_c == IDLE)beginscl <= 1'b1;end//只有在初始發(fā)送起始位時(shí)滿(mǎn)足拉低條件時(shí)不拉低,其余情況下滿(mǎn)足條件均要拉低;else if((((state_c == W_DEVICE_ADDR) && bit_cnt > 0) || (state_c != W_DEVICE_ADDR)) && h2l_flag)beginscl <= 1'b0;endend//賦值輸出信號(hào);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;sda_out <= 1'b1;endelse begincase (state_c)W_DEVICE_ADDR : beginif((~bit_cnt[3]) && wr_flag)//輸出器件地址和寫(xiě)指示位;sda_out <= device_addr[8 - bit_cnt];endW_REG_ADDR : beginif((~bit_cnt[3]) && wr_flag)//輸出需要寫(xiě)入的寄存器地址;sda_out <= reg_addr_r[REG_ADDR_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt];//reg_addr_r[7 - bit_cnt];endWDATA : begin//輸出寫(xiě)數(shù)據(jù),先輸出高字節(jié)數(shù)據(jù);if((~bit_cnt[3]) && wr_flag)sda_out <= wdata_r[DATA_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt];endR_DEVICE_ADDR : begin//輸出重復(fù)開(kāi)始信號(hào),器件地址和讀指示位;if(wr_flag)//當(dāng)SCL低電平時(shí)把SDA拉低,便于后續(xù)產(chǎn)生起始位;if(bit_cnt == 0 || bit_cnt == bit_cnt_num - 2)sda_out <= 1'b1;else//產(chǎn)生起始位之后,在SCL低電平中間發(fā)送器件地址;sda_out <= device_addr[8 - bit_cnt];else if(rd_flag && bit_cnt == 0)//在SCL高電平的時(shí)候拉低SDA,發(fā)送重復(fù)起始位;sda_out <= 1'b0;endRDATA : beginif(bit_cnt == bit_cnt_num - 1 && wr_flag)if(byte_cnt == DATA_BYTE_NUM - 1)//如果是讀取的最后一字節(jié)數(shù)據(jù),則不應(yīng)答。sda_out <= 1'b1;else//如果不是最后一字節(jié)數(shù)據(jù),則進(jìn)行應(yīng)答。sda_out <= 1'b0;endSTOP : beginif(wr_flag)//停止信號(hào)需要先拉低;sda_out <= 1'b0;else if(rd_flag)//在SCL高電平的時(shí)候拉高,表示停止位;sda_out <= 1'b1;enddefault : sda_out <= sda_out;endcaseendend//賦值輸出使能信號(hào),除了從機(jī)應(yīng)答之外,其余全為高電平;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為1;sda_out_en <= 1'b1;endelse if(wr_flag)begincase (state_c)//在寫(xiě)器件地址、寫(xiě)寄存器地址、寫(xiě)數(shù)據(jù)、讀過(guò)程的寫(xiě)器件地址的從機(jī)應(yīng)答狀態(tài),都需要釋放總線(xiàn);W_DEVICE_ADDR,WDATA,R_DEVICE_ADDR,W_REG_ADDR : beginif(bit_cnt == 0)//當(dāng)計(jì)數(shù)器為0時(shí),總線(xiàn)拉高,開(kāi)始寫(xiě)入下一字節(jié)數(shù)據(jù);sda_out_en <= 1'b1;else if(bit_cnt == bit_cnt_num - 1)//當(dāng)寫(xiě)入最后一位數(shù)據(jù)后,將使能信號(hào)拉低,釋放總線(xiàn);sda_out_en <= 1'b0;endSTOP : beginif(bit_cnt == 0)//當(dāng)計(jì)數(shù)器為0時(shí),總線(xiàn)拉高,開(kāi)始寫(xiě)入下一字節(jié)數(shù)據(jù);sda_out_en <= 1'b1;endRDATA : begin//在讀數(shù)據(jù)階段,主機(jī)應(yīng)答時(shí)需要控制總線(xiàn),其余時(shí)間釋放總線(xiàn);if(bit_cnt == 0)sda_out_en <= 1'b0;else if(bit_cnt == bit_cnt_num - 1)sda_out_en <= 1'b1;enddefault: ;endcaseendend//在讀數(shù)據(jù)階段,讀取總線(xiàn)上的數(shù)據(jù);always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;rdata_r <= 0;end//當(dāng)處于讀數(shù)據(jù)階段時(shí),在SCL高電平中間讀取數(shù)據(jù)總線(xiàn)上的數(shù)據(jù);else if(state_c == RDATA && rd_flag)beginrdata_r[DATA_BYTE_NUM*8 - 1 - byte_cnt*8 - bit_cnt] <= sda_in;endend//數(shù)據(jù)輸出有效指示信號(hào),該信號(hào)為高電平時(shí),表示讀取的數(shù)據(jù)rdata有效;always@(posedge clk)beginrdata_vld_r <= (state_c == RDATA) && rd_flag && (bit_cnt == bit_cnt_num - 2) && (byte_cnt == byte_cnt_num);end//將讀取的數(shù)據(jù)輸出。always@(posedge clk)beginrdata <= rdata_vld_r ? rdata_r : rdata;rdata_vld <= rdata_vld_r;end//模塊忙閑指示信號(hào),當(dāng)模塊接收到開(kāi)始信號(hào)或者狀態(tài)機(jī)不處于空閑狀態(tài)時(shí)拉低,表示模塊處于工作狀態(tài);always@(*)beginif(start || (state_c != IDLE))rdy = 1'b0;else//其余時(shí)間拉高,表示模塊處于空閑狀態(tài),上游模塊可以發(fā)起寫(xiě)或者讀操作;rdy = 1'b1;end//從機(jī)應(yīng)答失敗標(biāo)志信號(hào),高電平表示應(yīng)答失敗,每次開(kāi)始讀寫(xiě)操作時(shí)清零;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值為0;ack_flag <= 1'b0;endelse if(start)begin//接收到開(kāi)始讀寫(xiě)請(qǐng)求信號(hào)時(shí)拉低;ack_flag <= 1'b0;end//在從機(jī)應(yīng)答狀態(tài)下,將接收到的應(yīng)答信號(hào)輸出,高電平表示應(yīng)答失敗,低電平表示應(yīng)答成功。else if(((state_c == WDATA) || (state_c == W_DEVICE_ADDR) || (state_c == W_REG_ADDR) || (state_c == R_DEVICE_ADDR)) && rd_flag && (bit_cnt == bit_cnt_num - 1))beginack_flag <= sda_in;endendendmodule

??本文就這么多吧,后文對(duì)該模塊進(jìn)行仿真和上板驗(yàn)證,不是說(shuō)還沒(méi)有驗(yàn)證,是本文篇幅已經(jīng)過(guò)長(zhǎng)了,仿真也包括單字節(jié)讀、寫(xiě),頁(yè)寫(xiě)和頁(yè)讀,還有eeprom自己的內(nèi)容,涉及的東西也不會(huì)少。

??您的支持是我更新的最大動(dòng)力!將持續(xù)更新工程,如果本文對(duì)您有幫助,還請(qǐng)多多點(diǎn)贊👍、評(píng)論💬和收藏?!

http://www.risenshineclean.com/news/50208.html

相關(guān)文章:

  • 泰安集團(tuán)網(wǎng)站建設(shè)元搜索引擎有哪些
  • 個(gè)人攝影網(wǎng)站制作設(shè)計(jì)培訓(xùn)學(xué)院
  • 鄧州企業(yè)網(wǎng)站有鏈接的網(wǎng)站
  • 商城網(wǎng)站建設(shè)預(yù)算免費(fèi)網(wǎng)頁(yè)制作網(wǎng)站
  • magento建站是傻瓜式的嗎今日疫情最新情況
  • 笑話(huà)類(lèi)網(wǎng)站用什么做網(wǎng)絡(luò)運(yùn)營(yíng)怎么做
  • 長(zhǎng)沙市網(wǎng)站建設(shè)推廣谷歌推廣技巧
  • 南京專(zhuān)業(yè)網(wǎng)站建設(shè)整合營(yíng)銷(xiāo)傳播的概念
  • 世紀(jì)佳緣網(wǎng)站開(kāi)發(fā)公司網(wǎng)站大全軟件下載
  • 上海的外貿(mào)公司排名搜索引擎優(yōu)化不包括
  • 成都網(wǎng)站制作怎么樣網(wǎng)絡(luò)營(yíng)銷(xiāo)網(wǎng)站建設(shè)
  • 計(jì)算機(jī)應(yīng)用軟件開(kāi)發(fā)百度愛(ài)采購(gòu)優(yōu)化排名軟件
  • 公司手機(jī)網(wǎng)站制作seo收費(fèi)標(biāo)準(zhǔn)
  • 食品網(wǎng)站建設(shè)方案項(xiàng)目書(shū)百度推廣獲客成本大概多少
  • wordpress搜索標(biāo)簽頁(yè)seo推廣軟件排行榜前十名
  • 一家只做代購(gòu)的網(wǎng)站百度網(wǎng)盤(pán)app
  • 網(wǎng)站設(shè)計(jì)主題中文成都建設(shè)網(wǎng)官網(wǎng)
  • 最近一周新聞seo廣州工作好嗎
  • 備案網(wǎng)站內(nèi)容怎么寫(xiě)寧波seo關(guān)鍵詞排名
  • yourphp企業(yè)網(wǎng)站管理系統(tǒng)抖音的商業(yè)營(yíng)銷(xiāo)手段
  • WordPress 種子搜索seozou是什么意思
  • 汝南網(wǎng)站建設(shè)邵陽(yáng)做網(wǎng)站的公司
  • 優(yōu)秀的手機(jī)網(wǎng)站品牌全案策劃
  • wordpress 上傳下載seo教程免費(fèi)
  • 網(wǎng)站怎么做導(dǎo)航條南寧seo推廣服務(wù)
  • 網(wǎng)站設(shè)計(jì)制作開(kāi)發(fā)網(wǎng)銷(xiāo)是什么工作好做嗎
  • 網(wǎng)站圖片如何優(yōu)化權(quán)威解讀當(dāng)前經(jīng)濟(jì)熱點(diǎn)問(wèn)題
  • 怎么做垂直門(mén)戶(hù)網(wǎng)站百度廣告位價(jià)格
  • 玉林做網(wǎng)站優(yōu)化推廣天津百度推廣網(wǎng)絡(luò)科技公司
  • 找貨源的網(wǎng)上平臺(tái)有哪些合肥seo代理商