FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析
FPGA學(xué)習(xí)之路——I2C協(xié)議詳解+Verilog源碼分析
定義
I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半導(dǎo)體(現(xiàn)被NXP收購(gòu))開(kāi)發(fā)的兩線(xiàn)時(shí)串行總線(xiàn),常用于微控制器與外設(shè)之間的連接。I2C僅需兩根線(xiàn)就可以支持一主多從或者多主連接,主要優(yōu)點(diǎn)為簡(jiǎn)單、便宜、可靠性高,I2C總線(xiàn)示意圖如下。
- SDA(Serial Data):串行數(shù)據(jù)線(xiàn)
- SCL(Serial Clock):串行時(shí)鐘線(xiàn)
1、I2C總線(xiàn)共兩條雙向串行線(xiàn),SDA為串行數(shù)據(jù)線(xiàn),SCL為串行時(shí)鐘線(xiàn)。
2、SDA上的數(shù)據(jù)傳輸為最大端傳輸(先發(fā)送MSB,最后發(fā)送LSB),每次傳輸1個(gè)字節(jié)。
3、支持多主控,但任何時(shí)間只能有一個(gè)主控。
4、總線(xiàn)上每個(gè)設(shè)備都有自己的地址,共7個(gè)bit,第8個(gè)bit存放主設(shè)備對(duì)從設(shè)備的讀/寫(xiě)操作信息,廣播地址全0。
工作流程
1、I2C位傳輸
數(shù)據(jù)傳輸: SCL為高電平時(shí),若SDA線(xiàn)保持穩(wěn)定,那么SDA線(xiàn)上在進(jìn)行數(shù)據(jù)的傳輸或是空閑態(tài);若SDA線(xiàn)發(fā)生跳變,則表示一個(gè)會(huì)話(huà)的開(kāi)始或者結(jié)束。
數(shù)據(jù)改變: SDA僅能在SCL為低電平時(shí)改變傳輸?shù)腷it,否則表示會(huì)話(huà)狀態(tài)的改變。
2、I2C開(kāi)始和結(jié)束信號(hào)
開(kāi)始信號(hào): SCL為高電平時(shí),SDA由高電平向低電平跳變,開(kāi)始數(shù)據(jù)的傳送。
結(jié)束信號(hào): SCL為高電平時(shí),SDA由低電平向高電平跳變,結(jié)束數(shù)據(jù)的傳送。
3、I2C應(yīng)答信號(hào)
Master每發(fā)送完8bit數(shù)據(jù)后交出SDA的控制權(quán),等待Slave的ACK。也就是在第9個(gè)Clock,若從設(shè)備發(fā)ACK,那么SDA會(huì)被拉低。如果Master未收到從設(shè)備的ACK,那么SDA會(huì)被拉高,這會(huì)導(dǎo)致Master發(fā)生RESTART或者STOP流程。
4、I2C寫(xiě)流程
1、Master在SCL為高電平期間,拉低SDA,發(fā)起START。
2、Master發(fā)送設(shè)備地址(7bit)和寫(xiě)操作0(1bit),等待ACK。
3、對(duì)應(yīng)的Slave回應(yīng)ACK。
4、Master發(fā)送寄存器地址(8bit),等待ACK。
5、對(duì)應(yīng)的Slave回應(yīng)ACK。
6、Master發(fā)送數(shù)據(jù)(8bit),也就是要寫(xiě)入Slave寄存器中的數(shù)據(jù),等待ACK。
7、對(duì)應(yīng)的Slave回應(yīng)ACK。
8、其中的6,7步可重復(fù)執(zhí)行多次,即按順序?qū)Χ鄠€(gè)寄存器進(jìn)行寫(xiě)操作。
9、Master發(fā)起STOP。
5、I2C讀流程
1、Master在SCL為高電平期間,拉低SDA,發(fā)起START。
2、Master發(fā)送設(shè)備地址(7bit)和寫(xiě)操作0(1bit),等待ACK。
3、Slave發(fā)送ACK。
4、Master發(fā)送寄存器地址(8bit),等待ACK。
5、Slave發(fā)ACK。
6、Master發(fā)起START。
7、Master發(fā)送I2C設(shè)備地址(7bit)和讀操作1(1bit),等待ACK。
8、Slave發(fā)送ACK。
9、Slave發(fā)送data(以字節(jié)為單位),即對(duì)應(yīng)寄存器中的值。
10、Master發(fā)送ACK。
11、第9步和第10步可重復(fù)進(jìn)行多次,即按順序讀多個(gè)寄存器。
Verilog代碼分析
本博客中所示代碼片段為《VERILOG HDL應(yīng)用程序設(shè)計(jì)實(shí)例精講》提供的例程,僅供學(xué)習(xí)用途。
時(shí)鐘操作是I2C設(shè)計(jì)的關(guān)鍵部分,為更清楚的分析I2C時(shí)序關(guān)系,我們將一個(gè)時(shí)鐘周期分為4個(gè)部分a,b,c,d,如下圖所示。
Verilog代碼如下:
I2C總線(xiàn)START信號(hào)的特征:在時(shí)鐘信號(hào)SCL為高電平時(shí),數(shù)據(jù)信號(hào)SDA由高到低跳變,Verilog代碼如下:
assign sda=(link)? sda_buf:1'bz; //link為是否傳輸數(shù)據(jù)的標(biāo)志,sda_buf為sda的寄存器。//sda為雙向端口。 start:begincase(startcnt)2'b00:beginscl<=1'b1;sda_buf<=1'b1; //時(shí)鐘信號(hào)保持為高,sda數(shù)據(jù)設(shè)為高。link<=1'b1; //表示Master此時(shí)將sda_buf中的數(shù)據(jù)送到sda線(xiàn)上startcnt<=2'b01;end2'b01:beginscl<=1'b1; sda_buf<=1'b0; //時(shí)鐘信號(hào)保持為高,sda數(shù)據(jù)設(shè)為低,完成START。link<=1'b1; startcnt<=2'b10;end2'b10:beginscl<=1'b0; sda_buf<=1'b0; //時(shí)鐘信號(hào)和數(shù)據(jù)信號(hào)均為低。link<=1'b1; startcnt<=2'b11;end2'b11:beginscl<=1'b0; sda_buf<=1'b0; link<=1'b1; startcnt<=2'b00;inner_state<=first; //完成START操作后,進(jìn)行后續(xù)操作,改變外層狀態(tài)。enddefault:beginscl<=1'b1; sda_buf<=1'b1; link<=1'b1; startcnt<=2'b00;inner_state<=start;endendcase endI2C主設(shè)備發(fā)送從設(shè)備的地址信號(hào)和讀寫(xiě)標(biāo)志,其中地址信號(hào)的發(fā)送順序?yàn)閺母呶恢恋臀弧?/p> assign sda=(link)? sda_buf:1'bz; //link為是否傳輸數(shù)據(jù)的標(biāo)志,sda_buf為sda的寄存器。//sda為雙向端口。 case(startcnt)2'b00:beginscl<=1'b0; //此時(shí)時(shí)鐘線(xiàn)為低,無(wú)法進(jìn)行數(shù)據(jù)傳輸。sda_buf<=chipaddr[7]; //從設(shè)備地址最高位link<=1'b1;startcnt<=2'b01;end 2'b01:beginscl<=1'b1; //此時(shí)時(shí)鐘線(xiàn)為高,進(jìn)行數(shù)據(jù)傳輸。sda_buf<=chipaddr[7]; //從設(shè)備地址最高位link<=1'b1;startcnt<=2'b10;end2'b10:beginscl<=1'b1; sda_buf<=chipaddr[7]; //scl為高時(shí),sda需維持不變,否則會(huì)開(kāi)始或終止當(dāng)前會(huì)話(huà)。link<=1'b1;startcnt<=2'b11;end2'b11:beginscl<=1'b0; sda_buf<=chipaddr[7]; link<=1'b1;startcnt<=2'b00;inner_state<=second; //進(jìn)行下一步操作,傳輸?shù)刂返拇胃遙it。enddefault:beginscl<=1'b1; sda_buf<=chipaddr[7]; link<=1'b1;startcnt<=2'b00;inner_state<=first;end endcase
上面的代碼用于發(fā)送從設(shè)備的地址和讀寫(xiě)標(biāo)志,重復(fù)7次即可完成該操作。發(fā)送完從設(shè)備的地址信號(hào)的讀寫(xiě)標(biāo)志后,接著Master需檢測(cè)從設(shè)備發(fā)送的應(yīng)答信號(hào),Verilog代碼如下。
assign sda=(link)? sda_buf:1'bz; //link為是否傳輸數(shù)據(jù)的標(biāo)志,sda_buf為sda的寄存器。//sda為雙向端口。 ack:begincase(startcnt)2'b00:beginscl<=1'b0;link<=1'b0; //更改數(shù)據(jù)信號(hào)sda為輸入。startcnt<=2'b01;end2'b01:beginscl<=1'b1;link<=1'b0; startcnt<=2'b10;end2'b10:beginscl<=1'b1;link<=1'b0; sta_buf<=sda; //在時(shí)鐘線(xiàn)保持高電平時(shí),采樣數(shù)據(jù)信號(hào)sda的值。 startcnt<=2'b11;end2'b11:beginscl<=1'b0;link<=1'b0;startcnt<=2'b00;if(sda_buf==1'b0) //若接收的sda數(shù)據(jù)為低,則表示檢測(cè)到從設(shè)備的應(yīng)答信號(hào)begininner_state<=first; //返回初始狀態(tài)i2c_state<=sendaddr; //發(fā)送從設(shè)備地址成功后,進(jìn)入下一階段。//發(fā)送寄存器地址。link<=1'b1; //主設(shè)備重新在sda數(shù)據(jù)線(xiàn)上進(jìn)行數(shù)據(jù)傳輸。endelse beginmain_state<=3'b000; //回到等待讀寫(xiě)請(qǐng)求狀態(tài)。inner_state<=start;endendendcase endI2C總線(xiàn)停止信號(hào)的特征:在時(shí)鐘信號(hào)scl為高電平時(shí),sda由低到高進(jìn)行跳變,Verilog代碼如下。
assign sda=(link)? sda_buf:1'bz; //link為是否傳輸數(shù)據(jù)的標(biāo)志,sda_buf為sda的寄存器。//sda為雙向端口。 stop:begincase(startcnt)2'b00:begin //時(shí)鐘和數(shù)據(jù)信號(hào)都為低scl<=1'b0;sda_buf<=1'b0;link<=1'b1;startcnt<=2'b01;end2'b01:begin //時(shí)鐘信號(hào)變?yōu)楦?#xff0c;數(shù)據(jù)信號(hào)為低scl<=1'b1;sda_buf<=1'b0;link<=1'b1;startcnt<=2'b10;end2'b10:begin //不在此周期內(nèi)改變數(shù)據(jù)信號(hào)。//保證時(shí)鐘信號(hào)穩(wěn)定后再進(jìn)行數(shù)據(jù)信號(hào)的變化.scl<=1'b1;sda_buf<=1'b0;link<=1'b1;startcnt<=2'b11;end2'b11:begin //時(shí)鐘信號(hào)保持高,數(shù)據(jù)信號(hào)設(shè)為高,完成從低到高的跳變。scl<=1'b1;sda_buf<=1'b1;link<=1'b1;startcnt<=2'b00;inner_state<=start;i2c_state<=ini;main_state<=2'b00;enddefault:beginscl<=1'b1;sda_buf<=1'b0;link<=1'b1;startcnt<=2'b00;inner_state<=ack;endendcase end將以上的代碼段組合起來(lái),基本就可以實(shí)現(xiàn)I2C協(xié)議了。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: unix-ln 命令
- 下一篇: WWDC 2013 Session笔记