加入星計(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)期合作伙伴
立即加入
  • 正文
    • 1、I2C(Inter-Integrated Circuit)通信協(xié)議及FPGA模塊設(shè)計(jì)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

I2C通信模塊的設(shè)計(jì)和“AT24C64 型號(hào)的EEPROM 芯片通信”實(shí)踐

12小時(shí)前
276
閱讀需 15 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

# 期望通過(guò)本文掌握I2C模塊的FPGA軟件設(shè)計(jì)和實(shí)踐 #

1、I2C(Inter-Integrated Circuit)通信協(xié)議及FPGA模塊設(shè)計(jì)

I2C 是很常見(jiàn)的一種總線協(xié)議,使用兩條線在主控制器和從機(jī)之間進(jìn)行數(shù)據(jù)通信。一條是 SCL(串行時(shí)鐘線),另外一條是 SDA(串行數(shù)據(jù)線)。這兩條線都需要接上拉電 阻。因?yàn)閮H有一根數(shù)據(jù)線,所以I2C通信是半雙工的。

I2C 總線有標(biāo)準(zhǔn)模式(100kb/s)和快速模式(400kb/s)兩種。

“總線”指多個(gè)設(shè)備共用的信號(hào)線。在一個(gè)I2C總線中,支持多個(gè)從設(shè)備。不同的從設(shè) 備有不同的器件地址,這樣 I2C 主控制器就可以通過(guò) I2C 設(shè)備的器件地址訪問(wèn)指定的 I2C 設(shè)備了,一個(gè)I2C總線連接多個(gè) I2C 設(shè)備,如下所示,

I2C協(xié)議基本術(shù)語(yǔ)

起始信號(hào): I2C 通信起始標(biāo)志。主機(jī)告訴從機(jī),要開(kāi)始進(jìn)行 I2C 通信了。在 SCL 為高電平期間, SDA 出現(xiàn)下降沿就表示產(chǎn)生起始信號(hào)。起始信號(hào)產(chǎn)生后總線處于占用狀態(tài)。

停止信號(hào):I2C 停止通信的標(biāo)志。在 SCL 為高電平期間, SDA 出現(xiàn)上升沿就表示為停止信號(hào)。停止信號(hào)產(chǎn)生后總線被釋放,處于空閑狀態(tài)。

數(shù)據(jù)傳輸:I2C 總線在進(jìn)行數(shù)據(jù)傳輸時(shí)要保證在 SCL 高電平期間, SDA 上的數(shù)據(jù)穩(wěn)定,因此 SDA 上的數(shù)據(jù)變化只能在 SCL 低電平期間發(fā)生。

數(shù)據(jù)傳送時(shí),先傳送最高位,后傳送低位。

應(yīng)答信號(hào):當(dāng) I2C 主機(jī)發(fā)送完 8bit 數(shù)據(jù)后會(huì)將 SDA 設(shè)置為輸入狀態(tài),等待 I2C

從機(jī)應(yīng)答,也就是等到 I2C 從機(jī)告訴主機(jī)它接收到 8bit 數(shù)據(jù)了。

應(yīng)答信號(hào)是由從機(jī)發(fā)出的,主機(jī)需要提供應(yīng)答信號(hào)所需的時(shí)鐘。主機(jī)發(fā)送完數(shù)據(jù)以后的下一個(gè)時(shí)鐘信號(hào)就是給應(yīng)答信號(hào)使用的。從機(jī)通過(guò)將 SDA 拉低來(lái)表示發(fā)出應(yīng)答信號(hào)(ACK ,低有效),表示通信成功,否則表示通信失敗(NACK)。

設(shè)計(jì)一個(gè) I2C 模塊,要求如下所示,

    • 支持總線仲裁丟失檢測(cè);
    • 支持總線忙狀態(tài)檢測(cè);
    • 支持不同的 I2C 通信模式:
    • 標(biāo)準(zhǔn)模式(100kHz);
    • 快速模式(400kHz);
    • 支持產(chǎn)生起始、終止、重復(fù)起始和應(yīng)答信息;
    • 支持起始、終止和重復(fù)起始檢測(cè);
    • 支持 7 位尋址模式;
    • 支持中斷;

主要包括 8 個(gè) 8 位寬的寄存器,如下所示

    • I2C 分頻值低字節(jié)寄存器 I2C 分頻值高字節(jié)寄存器 I2C 控制寄存器
    • I2C 發(fā)送數(shù)據(jù)寄存器
    • I2C 接受數(shù)據(jù)寄存器
    • I2C 命令寄存器
    • I2C 狀態(tài)寄存器
    • I2C 總線死鎖時(shí)間寄存器

根據(jù) I2C 協(xié)議,我們需要設(shè)計(jì)一個(gè)狀態(tài)機(jī),對(duì)應(yīng) I2C 的通信過(guò)程,各個(gè)狀態(tài)如下所示,

根據(jù) I2C 寄存器的配置以及狀態(tài)完成標(biāo)志進(jìn)行上述狀態(tài)的切換,如下所示(僅展示部分代碼),

每個(gè)狀態(tài)都需要持續(xù)多個(gè)周期,所以針對(duì)每個(gè)狀態(tài)細(xì)分了幾個(gè)子狀態(tài),如下所示,

I2C 有 2 個(gè)外部接口,分別是 I2C_SCL I2C_SDA 。對(duì)于 I2C_SCL ,這里我們只設(shè)計(jì) I2C 主模式,所以對(duì)于 FPGA 來(lái)說(shuō), I2C_SCL 始終是輸出。對(duì)于 I2C_SDA ,在主模式下,在接收響應(yīng)的狀態(tài)下是輸入,其他情況下為輸出。為了便于接口管理,我們將 I2C_SCL I2C_SDA 都設(shè)計(jì)為 IOBUF 。如下所示,

主機(jī)讀寫(xiě)數(shù)據(jù)的時(shí)序操作如下所示,

主機(jī)寫(xiě)數(shù)據(jù)

  1. 主機(jī)操作命令寄存器,使能開(kāi)始命令,使 I2C 總線發(fā)送開(kāi)始信號(hào)。
  2. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入從機(jī)地址 + 讀寫(xiě)位,決定訪問(wèn)哪個(gè)從機(jī)。這是 一個(gè)8位的數(shù)據(jù),其中高 7 位是從機(jī)地址,最后 1 位是讀寫(xiě)位。 1 表示讀操作,0 表示寫(xiě)操作(對(duì)主機(jī)而言)。這里讀寫(xiě)位為 0 。
  3. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  4. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  5. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入從機(jī)存儲(chǔ)地址,決定待發(fā)送數(shù)據(jù)存儲(chǔ)在從機(jī)哪里。
  6. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  7. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  8. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入 8 bit 的待發(fā)送數(shù)據(jù)。
  9. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  10. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  11. 重復(fù)步驟 8 到 10,不斷向從機(jī)寫(xiě)數(shù)據(jù);
  12. 主機(jī)操作命令寄存器,使能結(jié)束命令,使 I2C 總線結(jié)束傳輸數(shù)據(jù)。
  13. 每次傳輸結(jié)束后需要延時(shí),保證下次能正常開(kāi)始傳輸。

主機(jī)讀數(shù)據(jù)

  1. 主機(jī)操作命令寄存器,使能開(kāi)始命令,使 I2C 總線發(fā)送開(kāi)始信號(hào)。
  2. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入從機(jī)地址 + 讀寫(xiě)位,決定訪問(wèn)哪個(gè)從機(jī)。這是 一個(gè)8位的數(shù)據(jù),其中高 7 位是從機(jī)地址,最后 1 位是讀寫(xiě)位。 1 表示讀操作,
    0 表示寫(xiě)操作(對(duì)主機(jī)而言)。這里讀寫(xiě)位為 0 。
  3. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  4. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  5. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入從機(jī)存儲(chǔ)地址,主機(jī)將會(huì)從該地址讀取數(shù)據(jù)。
  6. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  7. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  8. 主機(jī)操作命令寄存器,使能開(kāi)始命令(這種情況是重復(fù)起始),使 I2C 總線發(fā)送開(kāi)始信號(hào)。
  9. 主機(jī)操作發(fā)送數(shù)據(jù)寄存器,寫(xiě)入從機(jī)地址 + 讀寫(xiě)位。這里讀寫(xiě)位為 1。
  10. 主機(jī)操作命令寄存器,使能寫(xiě)命令,使 I2C 總線開(kāi)始傳輸數(shù)據(jù)。
  11. 主機(jī)讀取狀態(tài)寄存器的 TIP 位,以確保命令執(zhí)行完畢。
  12. 主機(jī)操作命令寄存器,使能讀命令和應(yīng)答命令,使 I2C 總線開(kāi)始接收數(shù)據(jù)。
  13. 主機(jī)操作接收數(shù)據(jù)寄存器,讀取接收到的 8 bit 的數(shù)據(jù)。
  14. 重復(fù)步驟 12 到 13,不斷從從機(jī)讀數(shù)據(jù);
  15. 當(dāng)主機(jī)需要停止從從機(jī)讀數(shù)據(jù)時(shí),操作命令寄存器,使能讀命令,但不使能應(yīng)答命令,讀取最后一個(gè)字節(jié)的數(shù)據(jù)。

我們可以將 I2C 模塊作為一個(gè) APB 外設(shè),掛在 APB 總線上。那么就需要設(shè)計(jì)一個(gè) APB 接口來(lái)對(duì)寄存器進(jìn)行讀寫(xiě)操作。我們需要對(duì)每個(gè) APB 接口分配一個(gè)地址,這樣才能通過(guò)譯碼電路區(qū)分開(kāi)來(lái)不同的 APB 。在 config.h 文件中定了 9 路 APB 地址,默認(rèn)使用 APB0 作為 GPIO ,現(xiàn)在我們?yōu)?I2C 模塊分配 APB5 ,對(duì)應(yīng)地址為 0xbfe90000 ,如下所示,

//APB0
`define APB_SLV0_ADDR_BASE 32'hbfeb0000 //APB0 base address
`define APB_SLV0_ADDR_LEN 32'h0000ffff //APB0 length
//APB1
`define APB_SLV1_ADDR_BASE 32'hbfec0000 //APB1 base address
`define APB_SLV1_ADDR_LEN 32'h0000ffff //APB1 length
//APB2
`define APB_SLV2_ADDR_BASE 32'hbfed0000 //APB2 base address
`define APB_SLV2_ADDR_LEN 32'h0000ffff //APB3 length
//APB3
`define APB_SLV3_ADDR_BASE 32'hbfea0000 //APB3 base address
`define APB_SLV3_ADDR_LEN 32'h0000ffff //APB3 length
//APB4
`define APB_SLV4_ADDR_BASE 32'hbfe88000 //APB4 base address
`define APB_SLV4_ADDR_LEN 32'h00000fff //APB4 length
//APB5
`define APB_SLV5_ADDR_BASE 32'hbfe90000 //APB5 base address
`define APB_SLV5_ADDR_LEN 32'h0000ffff //APB5 length

最后實(shí)現(xiàn)的結(jié)構(gòu)框圖如下所示,

在頂層文件 godson_mcu_top.v 中例化我們?cè)O(shè)計(jì)的模塊,如下所示,

在約束文件中,將例化好的 I2C 的輸出引腳原理圖上的合適引腳進(jìn)行連接即可,如下所示,

2. 軟件設(shè)計(jì),基于 I2C 模塊與 AT24C64 的EEPROM 芯片通信

既然已經(jīng)設(shè)計(jì)好了硬件電路,我們就可以進(jìn)行軟件程序的編寫(xiě)了。在硬件 I2C 模塊設(shè)計(jì)過(guò)程中我們?yōu)?APB 分配的地址是 0xbfe90000 ,并且 I2C 相關(guān)寄存器的偏移地址是 0x00 0x01 0x02 0x03 0x04,所以軟件上需要對(duì)應(yīng)好。一個(gè)不錯(cuò)的方法是用結(jié)構(gòu)體指針來(lái)訪問(wèn)寄存器。由于這個(gè)結(jié)構(gòu)體指針使用頻率很高,所以通過(guò)宏定義進(jìn)行重命名 (I2C),如下所示,

編寫(xiě)的函數(shù)聲明如下所示,

然后可以基于 I2C 相函數(shù)編寫(xiě) I2C 讀寫(xiě) AT24C64 的函數(shù),如下所示,

返回值:無(wú)
說(shuō) 明:無(wú)
****************************************************************/ void AT24CXX_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{
soc_I2C_GenerateSTART(ENABLE);// 起始信號(hào) soc_I2C_SendData(DEV_ADDR | WRITE_CMD);// 器件尋址+讀/寫(xiě)選擇 soc_I2C_wait();
soc_I2C_SendData((uint8_t)((u16Addr >> 8) & 0xFF)); soc_I2C_wait();
soc_I2C_SendData((uint8_t)(u16Addr & 0xFF)); soc_I2C_wait();
soc_I2C_SendData(u8Data); soc_I2C_wait(); soc_I2C_GenerateSTOP(ENABLE);// 停止信號(hào)
soc_I2C_delay(20);// 需要延時(shí) 2u ,保證下次能正常開(kāi)始傳輸
}
/****************************************************************
函數(shù)名:x24Cxx_ReadByte
功 能:讀一個(gè)字節(jié)
參 數(shù):u16Addr要讀取的地址
返回值:u8Data讀出的數(shù)據(jù)
說(shuō) 明:無(wú)
****************************************************************/ uint8_t AT24CXX_ReadByte(uint16_t u16Addr)

{

uint8_t u8Data = 0; soc_I2C_GenerateSTART(ENABLE);// 起始信號(hào)

soc_I2C_SendData(DEV_ADDR | WRITE_CMD);// 器件尋址+讀/寫(xiě)選擇

這里需要注意的是,對(duì)芯片完成單字節(jié)寫(xiě)入或者頁(yè)面寫(xiě)入命令后需要延時(shí) 10ms 。因?yàn)樵O(shè)備在此期間將不會(huì)對(duì)新的命令作出響應(yīng)。延時(shí)之后再進(jìn)行讀命令才可以讀到剛寫(xiě)入的數(shù)據(jù)。

我們可以在 main.c 中調(diào)用相應(yīng)函數(shù)驗(yàn)證 I2C 通信,如下所示,

相關(guān)推薦

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