• 正文
    • 導(dǎo)讀
    • 三、I2C 協(xié)議的具體實(shí)現(xiàn)
  • 推薦器件
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

基于 FPGA 的模擬 I2C協(xié)議設(shè)計(jì)(中)

2023/12/18
2046
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

今天給大俠帶來基于FPGA的 模擬 I2C 協(xié)議設(shè)計(jì),由于篇幅較長(zhǎng),分三篇。今天帶來第二篇,中篇,I2C 協(xié)議的具體實(shí)現(xiàn)。話不多說,上貨。

導(dǎo)讀

I2C(Inter-Integrated Circuit),其實(shí)是 I2C Bus簡(jiǎn)稱,中文就是集成電路總線,它是一種串行通信總線,使用多主從架構(gòu),由飛利浦公司在1980年代為了讓主板、嵌入式系統(tǒng)或手機(jī)用以連接低速周邊設(shè)備而發(fā)展。I2C的正確讀法為“I平方C”("I-squared-C"),而“I二C”("I-two-C")則是另一種錯(cuò)誤但被廣泛使用的讀法。自2006年10月1日起,使用 I2C 協(xié)議已經(jīng)不需要支付專利費(fèi),但制造商仍然需要付費(fèi)以獲取 I2C 從屬設(shè)備地址。

I2C 簡(jiǎn)單來說,就是一種串行通信協(xié)議,I2C的通信協(xié)議和通信接口在很多工程中有廣泛的應(yīng)用,如數(shù)據(jù)采集領(lǐng)域的串行 AD,圖像處理領(lǐng)域的攝像頭配置,工業(yè)控制領(lǐng)域的 X 射線管配置等等。除此之外,由于 I2C 協(xié)議占用的 IO 資源特別少,連接方便,所以工程中也常選用 I2C 接口做為不同芯片間的通信協(xié)議。I2C 串行總線一般有兩根信號(hào)線,一根是雙向的數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL。所有接到 I2C 總線設(shè)備上的串行數(shù)據(jù)SDA都接到總線的SDA上,各設(shè)備的時(shí)鐘線SCL接到總線的SCL上。

在現(xiàn)代電子系統(tǒng)中,有為數(shù)眾多的 IC 需要進(jìn)行相互之間以及與外界的通信。為了簡(jiǎn)化電路的設(shè)計(jì),Philips 公司開發(fā)了一種用于內(nèi)部 IC 控制的簡(jiǎn)單的雙向兩線串行總線 I2C(Intel-Integrated Circuit bus)。1998 年當(dāng)推出 I2C 總線協(xié)議 2.0 版本時(shí),I2C 協(xié)議實(shí)際上已經(jīng)成為一個(gè)國(guó)際標(biāo)準(zhǔn)。

在進(jìn)行 FPGA 設(shè)計(jì)時(shí),經(jīng)常需要和外圍提供 I2C 接口的芯片通信。例如低功耗CMOS 實(shí)時(shí)時(shí)鐘/日歷芯片 PCF8563、LCD 驅(qū)動(dòng)芯片 PCF8562、并行口擴(kuò)展芯片 PCF8574、鍵盤/LED 驅(qū)動(dòng)器 ZLG7290 等都提供 I2C 接口。因此在 FPGA 中模擬 I2C 接口已成為 FPGA 開發(fā)必要的步驟。

本篇將詳細(xì)講解在 FPGA 芯片中使用 VHDL/Verilog HDL 模擬 I2C 協(xié)議,以及編寫 TestBench仿真和測(cè)試程序的方法。

第二篇內(nèi)容摘要:本篇會(huì)介紹 I2C 協(xié)議的具體實(shí)現(xiàn),包括位傳輸?shù)膶?shí)現(xiàn)、字節(jié)傳輸?shù)膶?shí)現(xiàn)以及程序主體的實(shí)現(xiàn)等相關(guān)內(nèi)容。

三、I2C 協(xié)議的具體實(shí)現(xiàn)

FPGA 設(shè)計(jì)一般按照從頂向下的模式進(jìn)行:首先設(shè)計(jì)芯片功能,規(guī)劃各個(gè)模塊功能;然后按照規(guī)劃實(shí)現(xiàn)各個(gè)模塊。本篇由 3 個(gè)代碼文件組成:i2c_master_bit_ctrl.v 完成位傳輸?shù)墓δ?、i2c_master_byte_ctrl.v 完成字節(jié)傳輸?shù)墓δ?、i2c_master_top.v 完成整個(gè)程序的控制功能,并提供給外部程序的接口。在 ISE 中創(chuàng)建一個(gè)項(xiàng)目,然后加入上面 3 個(gè)文件。下面依次介紹 3 個(gè)文件的內(nèi)容。本篇講解采用 Verilog HDL。

3.1 位傳輸?shù)膶?shí)現(xiàn)

i2c_master_bit_ctrl.v 完成位傳輸?shù)墓δ?。位傳輸?shù)墓δ馨〝?shù)據(jù)按位傳輸?shù)膶?shí)現(xiàn)和 I2C協(xié)議各個(gè)命令的實(shí)現(xiàn)兩部分。

如圖 5 所示開始和重復(fù)開始命令的產(chǎn)生包括 5 個(gè)階段:idle 和 A、B、C、D 等。停止命令包括 4 個(gè)階段:idle 和 A、B、C 等。讀、寫一個(gè)字節(jié)通過 8 次位操作完成。

圖 5 位傳輸完成數(shù)據(jù)的傳輸和各個(gè)命令的實(shí)現(xiàn)

實(shí)現(xiàn)代碼如下:

`include "timescale.v"
`include "i2c_master_defines.v"

//模塊名稱及 IO
module i2c_master_bit_ctrl(
clk, rst, nReset,
clk_cnt, ena, cmd, cmd_ack, busy, al, din, dout,
scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen
);

// 輸入、輸出
input clk;
input rst;
input nReset;
input ena; // 模塊使能信號(hào)
input [15:0] clk_cnt; // 時(shí)鐘分頻系數(shù)
input [3:0] cmd;
output cmd_ack; // 命令完成應(yīng)答
reg cmd_ack;
output busy; // 總線忙
reg busy;
output al; // 總線仲裁丟失
reg al;
input din;
output dout;
reg dout;

// I2C 連線
input scl_i; // I2C 時(shí)鐘輸入
output scl_o; // I2C 時(shí)鐘輸出
output scl_oen; // I2C 時(shí)鐘輸出使能
reg scl_oen;
input sda_i; //I2C 數(shù)據(jù)輸入
output sda_o; // I2C 數(shù)據(jù)輸出
output sda_oen; // I2C 數(shù)據(jù)輸出使能
reg sda_oen;

// variable declarations
reg sSCL, sSDA; // 同步后的 SCL 和 SDA 輸入
reg dscl_oen; // 延遲后的 scl_oen
reg sda_chk; // 檢 查 后 的 SDA output (Multi-master
arbitration)
reg clk_en; // 時(shí)鐘產(chǎn)生信號(hào)
wire slave_wait;
reg [15:0] cnt; // 時(shí)鐘分頻計(jì)數(shù)器

// 模塊主體
// 當(dāng)從節(jié)點(diǎn)沒有準(zhǔn)備好時(shí),下拉 SCL 來延遲周期
// 延遲 scl_oen
always @(posedge clk)
dscl_oen <= #1 scl_oen;

assign slave_wait = dscl_oen && !sSCL;

// 產(chǎn)生時(shí)鐘使能信號(hào)
always @(posedge clk or negedge nReset)
if(~nReset)
begin
cnt <= #1 16'h0;
clk_en <= #1 1'b1;
end
else if (rst)
begin
cnt <= #1 16'h0;
clk_en <= #1 1'b1;
end
else if ( ~|cnt || ~ena)
if (~slave_wait)
begin
cnt <= #1 clk_cnt;
clk_en <= #1 1'b1;
end
else
begin
cnt <= #1 cnt;
clk_en <= #1 1'b0;
end
else
begin
cnt <= #1 cnt - 16'h1;
clk_en <= #1 1'b0;
end

// 產(chǎn)生總線狀態(tài)控制信號(hào)
reg dSCL, dSDA;
reg sta_condition;
reg sto_condition;

// 同步 SCL 和 SDA 輸入信號(hào),減少不穩(wěn)定風(fēng)險(xiǎn)
always @(posedge clk or negedge nReset)
if (~nReset)
begin
sSCL <= #1 1'b1;
sSDA <= #1 1'b1;
dSCL <= #1 1'b1;
dSDA <= #1 1'b1;
end
else if (rst)
begin
sSCL <= #1 1'b1;
sSDA <= #1 1'b1;
dSCL <= #1 1'b1;
dSDA <= #1 1'b1;
end
else
begin
sSCL <= #1 scl_i;
sSDA <= #1 sda_i;
dSCL <= #1 sSCL;
dSDA <= #1 sSDA;
end

// SCL 處于高時(shí)檢測(cè)到 SDA 的下降沿,即檢測(cè)開始狀態(tài)信號(hào)
// SCL 處于高時(shí)檢測(cè)到 SDA 的上升沿,即檢測(cè)停止?fàn)顟B(tài)信號(hào)
always @(posedge clk or negedge nReset)
if (~nReset)
begin
sta_condition <= #1 1'b0;
sto_condition <= #1 1'b0;
end
else if (rst)
begin
sta_condition <= #1 1'b0;
sto_condition <= #1 1'b0;
end
else
begin
sta_condition <= #1 ~sSDA & dSDA & sSCL;
sto_condition <= #1 sSDA & ~dSDA & sSCL;
end

// 產(chǎn)生 I2C 總線忙信號(hào)
always @(posedge clk or negedge nReset)
if(!nReset)
busy <= #1 1'b0;
else if (rst)
busy <= #1 1'b0;
else
busy <= #1 (sta_condition | busy) & ~sto_condition;

// 產(chǎn)生仲裁丟失信號(hào) generate arbitration lost signal
// 仲裁丟失發(fā)生在:
// 1) 主節(jié)點(diǎn)驅(qū)動(dòng) SDA 處于高,但是 I2C 總線一直處于低
// 2) 沒有請(qǐng)求時(shí)卻檢測(cè)到停止?fàn)顟B(tài)信號(hào)
reg cmd_stop, dcmd_stop;

always @(posedge clk or negedge nReset)
if (~nReset)
begin
cmd_stop <= #1 1'b0;
dcmd_stop <= #1 1'b0;
al <= #1 1'b0;
end
else if (rst)
begin
cmd_stop <= #1 1'b0;
dcmd_stop <= #1 1'b0;
al <= #1 1'b0;
end
else
begin
cmd_stop <= #1 cmd == `I2C_CMD_STOP;
dcmd_stop <= #1 cmd_stop;
al <= #1 (sda_chk & ~sSDA & sda_oen) | (sto_condition & ~dcmd_stop);
end

// 產(chǎn)生數(shù)據(jù)輸出信號(hào),在 SCL 信號(hào)的上升沿保存 SDA
always @(posedge clk)
if(sSCL & ~dSCL)
dout <= #1 sSDA;

// 產(chǎn)生狀態(tài)機(jī)
// 狀態(tài)譯碼
parameter [16:0] idle = 17'b0_0000_0000_0000_0000;
parameter [16:0] start_a = 17'b0_0000_0000_0000_0001;
parameter [16:0] start_b = 17'b0_0000_0000_0000_0010;
parameter [16:0] start_c = 17'b0_0000_0000_0000_0100;
parameter [16:0] start_d = 17'b0_0000_0000_0000_1000;
parameter [16:0] start_e = 17'b0_0000_0000_0001_0000;
parameter [16:0] stop_a = 17'b0_0000_0000_0010_0000;
parameter [16:0] stop_b = 17'b0_0000_0000_0100_0000;
parameter [16:0] stop_c = 17'b0_0000_0000_1000_0000;
parameter [16:0] stop_d = 17'b0_0000_0001_0000_0000;
parameter [16:0] rd_a = 17'b0_0000_0010_0000_0000;
parameter [16:0] rd_b = 17'b0_0000_0100_0000_0000;
parameter [16:0] rd_c = 17'b0_0000_1000_0000_0000;
parameter [16:0] rd_d = 17'b0_0001_0000_0000_0000;
parameter [16:0] wr_a = 17'b0_0010_0000_0000_0000;
parameter [16:0] wr_b = 17'b0_0100_0000_0000_0000;
parameter [16:0] wr_c = 17'b0_1000_0000_0000_0000;
parameter [16:0] wr_d = 17'b1_0000_0000_0000_0000;
reg [16:0] c_state;

//狀態(tài)機(jī)
always @(posedge clk or negedge nReset)
if (!nReset)
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b0;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end
else if (rst | al)
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b0;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end
else
begin
cmd_ack <= #1 1'b0;
if (clk_en)
case (c_state)
// idle 狀態(tài)
idle:
begin
case (cmd)
`I2C_CMD_START:
c_state <= #1 start_a;
`I2C_CMD_STOP:
c_state <= #1 stop_a;
`I2C_CMD_WRITE:
c_state <= #1 wr_a;
`I2C_CMD_READ:
c_state <= #1 rd_a;
default:
c_state <= #1 idle;
endcase
scl_oen <= #1 scl_oen; // 保持 SCL 在同一狀態(tài)
sda_oen <= #1 sda_oen; // 保持 SDA 在同一狀態(tài)
sda_chk <= #1 1'b0; // 不檢查 SDA 輸出
end
// 開始狀態(tài)
start_a:
begin
c_state <= #1 start_b;
scl_oen <= #1 scl_oen; // 保持 SCL 在同一狀態(tài)
sda_oen <= #1 1'b1; // 保持 SDA 處于高
sda_chk <= #1 1'b0; // 不檢查 SDA 的輸出
end

start_b:
begin
c_state <= #1 start_c;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end

start_c:
begin
c_state <= #1 start_d;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

start_d:
begin
c_state <= #1 start_e;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

start_e:
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b1;
scl_oen <= #1 1'b0;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

// 停止?fàn)顟B(tài)
stop_a:
begin
c_state <= #1 stop_b;
scl_oen <= #1 1'b0;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

stop_b:
begin
c_state <= #1 stop_c;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

stop_c:
begin
c_state <= #1 stop_d;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b0;
sda_chk <= #1 1'b0;
end

stop_d:
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b1;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end

// 讀狀態(tài)
rd_a:
begin
c_state <= #1 rd_b;
scl_oen <= #1 1'b0; //保持 SCL 處于低
sda_oen <= #1 1'b1; // SDA 處于三態(tài)
sda_chk <= #1 1'b0; // 不檢查 SDA 輸出
end

rd_b:
begin
c_state <= #1 rd_c;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end

rd_c:
begin
c_state <= #1 rd_d;
scl_oen <= #1 1'b1;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end

rd_d:
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b1;
scl_oen <= #1 1'b0;
sda_oen <= #1 1'b1;
sda_chk <= #1 1'b0;
end

// 寫狀態(tài)
wr_a:
begin
c_state <= #1 wr_b;
scl_oen <= #1 1'b0;
sda_oen <= #1 din;
sda_chk <= #1 1'b0;
end

wr_b:
begin
c_state <= #1 wr_c;
scl_oen <= #1 1'b1;
sda_oen <= #1 din;
sda_chk <= #1 1'b1;
end

wr_c:
begin
c_state <= #1 wr_d;
scl_oen <= #1 1'b1;
sda_oen <= #1 din;
sda_chk <= #1 1'b1;
end

wr_d:
begin
c_state <= #1 idle;
cmd_ack <= #1 1'b1;
scl_oen <= #1 1'b0;
sda_oen <= #1 din;
sda_chk <= #1 1'b0;
end

endcase
end

// 分配 SCL 和 SDA 輸出一直處于低
assign scl_o = 1'b0;
assign sda_o = 1'

endmodule

3.2 字節(jié)傳輸?shù)膶?shí)現(xiàn)

字節(jié)傳輸?shù)木唧w實(shí)現(xiàn)流程如圖 6 所示。

圖 6 字節(jié)傳輸控制模塊流程圖

字節(jié)傳輸控制模塊控制以字節(jié)為單位的數(shù)據(jù)傳輸。它根據(jù)命令寄存器的設(shè)置將數(shù)據(jù)傳輸寄存器中的內(nèi)容傳輸?shù)酵獠抗?jié)點(diǎn),將外部節(jié)點(diǎn)的數(shù)據(jù)接收到數(shù)據(jù)接收寄存器中。

實(shí)現(xiàn)代碼如下:

`include "timescale.v"
`include "i2c_master_defines.v"
//模塊
module i2c_master_byte_ctrl (
clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din,
cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen );

// 輸入、輸出
input clk; // 主時(shí)鐘
input rst; // 同步 RESET,高有效
input nReset; // 異步 RESET,低有效
input ena; // 模塊使能信號(hào)
input [15:0] clk_cnt; // 4 倍 SCL 信號(hào)

// 控制信號(hào)輸入
input start;
input stop;
input read;
input write;
input ack_in;
input [7:0] din;

// 狀態(tài)信號(hào)輸出
output cmd_ack;
reg cmd_ack;
output ack_out;
reg ack_out;
output i2c_busy;
output i2c_al;
output [7:0] dout;

// I2C 信號(hào)
input scl_i;
output scl_o;
output scl_oen;
input sda_i;
output sda_o;
output sda_oen;

// 變量申明
// 狀態(tài)機(jī)
parameter [4:0] ST_IDLE = 5'b0_0000;
parameter [4:0] ST_START = 5'b0_0001;
parameter [4:0] ST_READ = 5'b0_0010;
parameter [4:0] ST_WRITE = 5'b0_0100;
parameter [4:0] ST_ACK = 5'b0_1000;
parameter [4:0] ST_STOP = 5'b1_0000;

// 位控制模塊的信號(hào)
reg [3:0] core_cmd;
reg core_txd;
wire core_ack, core_rxd;

// 移位寄存器信號(hào)
reg [7:0] sr; //8 位移位寄存器
reg shift, ld;

// 狀態(tài)機(jī)信號(hào)
wire go;
reg [2:0] dcnt;
wire cnt_done;

// 模塊主體
// 連接位控制模塊
i2c_master_bit_ctrl bit_controller (
.clk ( clk ),
.rst ( rst ),
.nReset ( nReset ),
.ena ( ena ),
.clk_cnt ( clk_cnt ),
.cmd ( core_cmd ),
.cmd_ack ( core_ack ),
.busy ( i2c_busy ),
.al ( i2c_al ),
.din ( core_txd ),
.dout ( core_rxd ),
.scl_i ( scl_i ),
.scl_o ( scl_o ),
.scl_oen ( scl_oen ),
.sda_i ( sda_i ),
.sda_o ( sda_o ),
.sda_oen ( sda_oen )
);

// 產(chǎn)生 GO 信號(hào),當(dāng)讀/寫/停止/應(yīng)答時(shí)發(fā)生
assign go = (read | write | stop) & ~cmd_ack;

// 分配輸出到移位寄存器
assign dout = sr;

// 產(chǎn)生移位寄存器
always @(posedge clk or negedge nReset)
if (!nReset)
sr <= #1 8'h0;
else if (rst)
sr <= #1 8'h0;
else if (ld)
sr <= #1 din;
else if (shift)
sr <= #1 {sr[6:0], core_rxd};

// 產(chǎn)生計(jì)數(shù)器
always @(posedge clk or negedge nReset)
if (!nReset)
dcnt <= #1 3'h0;
else if (rst)
dcnt <= #1 3'h0;
else if (ld)
dcnt <= #1 3'h7;
else if (shift)
dcnt <= #1 dcnt - 3'h1;

assign cnt_done = ~(|dcnt);

// 狀態(tài)機(jī)
reg [4:0] c_state;
always @(posedge clk or negedge nReset)
if (!nReset)
begin
core_cmd <= #1 `I2C_CMD_NOP;
core_txd <= #1 1'b0;
shift <= #1 1'b0;
ld <= #1 1'b0;
cmd_ack <= #1 1'b0;
c_state <= #1 ST_IDLE;
ack_out <= #1 1'b0;
end
else if (rst | i2c_al)
begin
core_cmd <= #1 `I2C_CMD_NOP;
core_txd <= #1 1'b0;
shift <= #1 1'b0;
ld <= #1 1'b0;
cmd_ack <= #1 1'b0;
c_state <= #1 ST_IDLE;
ack_out <= #1 1'b0;
end
else
begin
// 初始化所有信號(hào)
core_txd <= #1 sr[7];
shift <= #1 1'b0;
ld <= #1 1'b0;
cmd_ack <= #1 1'b0;
case (c_state)
//IDLE 狀態(tài)
ST_IDLE:
if (go)
begin
if (start)
begin
c_state <= #1 ST_START;
core_cmd <= #1 `I2C_CMD_START;
end
else if (read)

begin
c_state <= #1 ST_READ;
core_cmd <= #1 `I2C_CMD_READ;
end
else if (write)
begin
c_state <= #1 ST_WRITE;
core_cmd <= #1 `I2C_CMD_WRITE;
end
else // 缺省的是 stop 狀態(tài)
begin
c_state <= #1 ST_STOP;
core_cmd <= #1 `I2C_CMD_STOP;
// 產(chǎn)生應(yīng)答信號(hào)
cmd_ack <= #1 1'b1;
end
ld <= #1 1'b1;
end

//開始狀態(tài)
ST_START:
if (core_ack)
begin
if (read)
begin
c_state <= #1 ST_READ;
core_cmd <= #1 `I2C_CMD_READ;
end
else
begin
c_state <= #1 ST_WRITE;
core_cmd <= #1 `I2C_CMD_WRITE;
end
ld <= #1 1'b1;
end

//寫數(shù)據(jù)狀態(tài)
ST_WRITE:
if (core_ack)
if (cnt_done)
begin
c_state <= #1 ST_ACK;
core_cmd <= #1 `I2C_CMD_READ;
end
else
begin
c_state <= #1 ST_WRITE; // 保持在原來狀態(tài)
core_cmd <= #1 `I2C_CMD_WRITE; // 寫下一位數(shù)據(jù)
shift <= #1 1'b1;
end

//讀信號(hào)狀態(tài)
ST_READ:
if (core_ack)
begin
if (cnt_done)
begin
c_state <= #1 ST_ACK;
core_cmd <= #1 `I2C_CMD_WRITE;
end
else
begin
c_state <= #1 ST_READ; // 保留在原來狀態(tài)
core_cmd <= #1 `I2C_CMD_READ; // 讀下一位數(shù)據(jù)
end
shift <= #1 1'b1;
core_txd <= #1 ack_in;
end

//應(yīng)答數(shù)據(jù)狀態(tài)
ST_ACK:
if (core_ack)
begin
if (stop)
begin
c_state <= #1 ST_STOP;
core_cmd <= #1 `I2C_CMD_STOP;
end
else
begin
c_state <= #1 ST_IDLE;
core_cmd <= #1 `I2C_CMD_NOP;
end

// 把應(yīng)答信號(hào)輸出連接到位控制模塊
ack_out <= #1 core_rxd;
// 產(chǎn)生應(yīng)答信號(hào)
cmd_ack <= #1 1'b1;
core_txd <= #1 1'b1;
end
else
core_txd <= #1 ack_in;

//停止?fàn)顟B(tài)
ST_STOP:
if (core_ack)
begin
c_state <= #1 ST_IDLE;
core_cmd <= #1 `I2C_CMD_NOP;
end
endcase
end

endmodule

3.3 程序主體的實(shí)現(xiàn)

程序主體部分完成與外部程序的接口、與總線上外部節(jié)點(diǎn)的連線、完成程序內(nèi)部各個(gè)寄存器的構(gòu)建、控制字節(jié)傳輸控制模塊等功能。代碼如下:

`include "timescale.v"
`include "i2c_master_defines.v"
//模塊定義
module i2c_master_top(
wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o,
wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o,
scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o );

// 參數(shù)
parameter ARST_LVL = 1'b0; // 異步 reset 信號(hào)

// 輸入、輸出信號(hào)
// 連接到外部接口的信號(hào)
input clk_i; // 主節(jié)點(diǎn)時(shí)鐘信號(hào)
input rst_i; // 同步 reset 信號(hào),高有效
input arst_i; // 異步 reset 信號(hào)
input [2:0] adr_i; // 低位地址信號(hào)
input [7:0] dat_i; // 數(shù)據(jù)總線輸入
output [7:0] dat_o; // 數(shù)據(jù)總線輸出
input we_i; // 輸入使能信號(hào)
input stb_i; // 觸發(fā)信號(hào)
input cyc_i; // 總線周期輸入
output ack_o; // 應(yīng)答信號(hào)輸出
output inta_o; // 中斷請(qǐng)求信號(hào)輸出
reg [7:0] wb_dat_o;
reg wb_ack_o;
reg wb_inta_o;

// I2C 信號(hào)
// I2C 時(shí)鐘信號(hào)線
input scl_pad_i; // SCL 輸入
output scl_pad_o; // SCL 輸出
output scl_padoen_o; // SCL 輸出使能

// I2C 數(shù)據(jù)線
input sda_pad_i; // SDA 輸入
output sda_pad_o; // SDA 輸出
output sda_padoen_o; // SDA 輸出使能

// 變量申明
// 寄存器
reg [15:0] prer; // 時(shí)鐘分頻寄存器
reg [ 7:0] ctr; // 控制寄存器
reg [ 7:0] txr; // 數(shù)據(jù)傳輸寄存器
wire [ 7:0] rxr; // 數(shù)據(jù)接收寄存器
reg [ 7:0] cr; // 命令寄存器
wire [ 7:0] sr; // 狀態(tài)寄存器

// 完成信號(hào),命令完成后清除命令寄存器
wire done;

// 模塊使能信號(hào)
wire core_en;
wire ien;

// 狀態(tài)寄存器信號(hào)
wire irxack;
reg rxack; // 從從節(jié)點(diǎn)接收應(yīng)答信號(hào)
reg tip; // 傳輸進(jìn)行標(biāo)志
reg irq_flag; // 中斷掛起標(biāo)志
wire i2c_busy; // 總線忙標(biāo)志
wire i2c_al; // 總線仲裁丟失
reg al; // 狀態(tài)寄存器仲裁丟失位

// 模塊主體
// 產(chǎn)生內(nèi)部 reset
wire rst_i = arst_i ^ ARST_LVL;
wire wacc = cyc_i & stb_i & we_i;

// 產(chǎn)生應(yīng)答輸出信號(hào)
always @(posedge clk_i)
wb_ack_o <= #1 cyc_i & stb_i & ~ack_o;

// 數(shù)據(jù)輸出
always @(posedge clk_i)
begin
case (adr_i)
3'b000: wb_dat_o = prer[ 7:0];
3'b001: wb_dat_o = prer[15:8];
3'b010: wb_dat_o = ctr;
3'b011: wb_dat_o = rxr; // 寫數(shù)據(jù)傳輸寄存器
3'b100: wb_dat_o = sr; // 寫命令寄存器
3'b101: wb_dat_o = txr;
3'b110: wb_dat_o = cr;
3'b111: wb_dat_o = 0; // 保留位
endcase
end

// 產(chǎn)生寄存器
always @(posedge wb_clk_i or negedge rst_i)
if (!rst_i)
begin
prer <= #1 16'hffff;
ctr <= #1 8'h0;
txr <= #1 8'h0;
end
else if (wb_rst_i)
begin
prer <= #1 16'hffff;
ctr <= #1 8'h0;
txr <= #1 8'h0;
end
else
if (wb_wacc)
case (wb_adr_i) // synopsis full_case parallel_case
3'b000 : prer [ 7:0] <= #1 wb_dat_i;
3'b001 : prer [15:8] <= #1 wb_dat_i;
3'b010 : ctr <= #1 wb_dat_i;
3'b011 : txr <= #1 wb_dat_i;
endcase

// 產(chǎn)生命令寄存器
always @(posedge wb_clk_i or negedge rst_i)
if (~rst_i)
cr <= #1 8'h0;
else if (wb_rst_i)
cr <= #1 8'h0;
else if (wb_wacc)
begin
if (core_en & (wb_adr_i == 3'b100) )
cr <= #1 wb_dat_i;
end
else
begin
if (done | i2c_al)
cr[7:4] <= #1 4'h0; // 命令完成或者仲裁丟失時(shí)清除命令寄存器內(nèi)容
cr[2:1] <= #1 2'b0; // 保留位
cr[0] <= #1 2'b0; // 清除 IRQ_ACK 位
end

// 譯碼命令寄存器
wire sta = cr[7];
wire sto = cr[6];
wire rd = cr[5];
wire wr = cr[4];
wire ack = cr[3];
wire iack = cr[0];

// 譯碼控制寄存器
assign core_en = ctr[7];
assign ien = ctr[6];

// 連接字節(jié)控制模塊
i2c_master_byte_ctrl byte_controller (
.clk ( wb_clk_i ),
.rst ( wb_rst_i ),
.nReset ( rst_i ),
.ena ( core_en ),
.clk_cnt ( prer ),
.start ( sta ),
.stop ( sto ),
.read ( rd ),
.write ( wr ),
.ack_in ( ack ),
.din ( txr ),
.cmd_ack ( done ),
.ack_out ( irxack ),
.dout ( rxr ),
.i2c_busy ( i2c_busy ),
.i2c_al ( i2c_al ),
.scl_i ( scl_pad_i ),
.scl_o ( scl_pad_o ),
.scl_oen ( scl_padoen_o ),
.sda_i ( sda_pad_i ),
.sda_o ( sda_pad_o ),
.sda_oen ( sda_padoen_o )
)

// 狀態(tài)寄存器部分和中斷請(qǐng)求信號(hào)
always @(posedge wb_clk_i or negedge rst_i)
if (!rst_i)
begin
al <= #1 1'b0;
rxack <= #1 1'b0;
tip <= #1 1'b0;
irq_flag <= #1 1'b0;
end
else if (wb_rst_i)
begin
al <= #1 1'b0;
rxack <= #1 1'b0;
tip <= #1 1'b0;
irq_flag <= #1 1'b0;
end
else
begin
al <= #1 i2c_al | (al & ~sta);
rxack <= #1 irxack;
tip <= #1 (rd | wr);
irq_flag <= #1 (done | i2c_al | irq_flag) & ~iack;
end

// 中斷請(qǐng)求標(biāo)志
// 產(chǎn)生中斷請(qǐng)求信號(hào)
always @(posedge wb_clk_i or negedge rst_i)
if (!rst_i)
wb_inta_o <= #1 1'b0;
else if (wb_rst_i)
wb_inta_o <= #1 1'b0;
else
wb_inta_o <= #1 irq_flag && ien; //中斷使能位 IEN 設(shè)置后產(chǎn)生中斷信號(hào)

assign sr[7] = rxack;
assign sr[6] = i2c_busy;
assign sr[5] = al;
assign sr[4:2] = 3'h0; // reserved
assign sr[1] = tip;
assign sr[0] = irq_flag;

endmodule

本篇到此結(jié)束,下一篇帶來基于 FPGA 的 模擬 I2C 協(xié)議設(shè)計(jì)(下),程序的仿真與測(cè)試,包括主節(jié)點(diǎn)的仿真、從節(jié)點(diǎn)的仿真、仿真主程序、仿真結(jié)果以及總結(jié)等相關(guān)內(nèi)容。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
EP4CE75F23I7N 1 Altera Corporation Field Programmable Gate Array, 4713 CLBs, 472.5MHz, 75408-Cell, PBGA484, 23 X 23 MM, 1 MM PITCH, LEAD FREE, FBGA-484
$272.95 查看
5M160ZE64I5N 1 Intel Corporation Flash PLD, 14ns, 128-Cell, CMOS, PQFP64, 9 X 9 MM, 0.40 MM PITCH, LEAD FREE, PLASTIC, EQFP-64

ECAD模型

下載ECAD模型
$4.8 查看
M4A5-64/32-10VNC 1 Lattice Semiconductor Corporation EE PLD, 10ns, 64-Cell, CMOS, PQFP44, 1 MM HEIGHT, LEAD FREE, TQFP-44

ECAD模型

下載ECAD模型
$7.2 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

任何技術(shù)的學(xué)習(xí)就好比一個(gè)江湖,對(duì)于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。