加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1、I2C(Inter-Integrated Circuit)通信協(xié)議及FPGA模塊設(shè)計
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

I2C通信模塊的設(shè)計和“AT24C64 型號的EEPROM 芯片通信”實踐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我們可以將 I2C 模塊作為一個 APB 外設(shè),掛在 APB 總線上。那么就需要設(shè)計一個 APB 接口來對寄存器進(jìn)行讀寫操作。我們需要對每個 APB 接口分配一個地址,這樣才能通過譯碼電路區(qū)分開來不同的 APB 。在 config.h 文件中定了 9 路 APB 地址,默認(rèn)使用 APB0 作為 GPIO ,現(xiàn)在我們?yōu)?I2C 模塊分配 APB5 ,對應(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

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

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

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

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

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

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

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

返回值:無
說 明:無
****************************************************************/ void AT24CXX_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{
soc_I2C_GenerateSTART(ENABLE);// 起始信號 soc_I2C_SendData(DEV_ADDR | WRITE_CMD);// 器件尋址+讀/寫選擇 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);// 停止信號
soc_I2C_delay(20);// 需要延時 2u ,保證下次能正常開始傳輸
}
/****************************************************************
函數(shù)名:x24Cxx_ReadByte
功 能:讀一個字節(jié)
參 數(shù):u16Addr要讀取的地址
返回值:u8Data讀出的數(shù)據(jù)
說 明:無
****************************************************************/ uint8_t AT24CXX_ReadByte(uint16_t u16Addr)

{

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

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

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

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

相關(guān)推薦

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