i2cバスマスター (i2c bus master)
i2cバスマスターのコードをverilogで記述してみます。 (Verilog I2C bus master)
1) 事前準備 (Preparation)
クロックは100kHzとするので、200kHzのカウンタを50MHzクロックから作成して、その立ち上がりエッジを検出します。
200kHzは50MHzを200分周して生成します。これはQuartus IIのメガファンクションPLLで作成しておきます。
モード変数として、mode_i2cを定義しておきます。
Preparation
output i2c_scl;
reg i2c_scl;
inout i2c_sda;
reg i2c_sda;
output error_flag;
reg error_flag;
reg [7:0] mode_i2c;
reg [7:0] counter;
reg rw_flag; // read = 1, write = 0;
always@(posedge clock_200kHz or negedge start_n)
begin
if (start_n == 0)
begin
mode_i2c = 0;
end
else
begin
ここに今回のi2cバスマスター制御を記述します。
end
end
2)イニシャルコンディション (Initial Condition)
イニシャルコンディションはSCL=1/SDA=1を記述します。
Initial Condition
case (mode_i2c)
0: // Initial Condition
begin
i2c_scl = 1;
ic2_sda = 1;
mode_i2c = 1;
end
3) スタートコンディション (Start Condition)
スタートコンディションはSCL=1/SDA=1からSCL=1/SDA=0, SCL=0/SDA=0へと遷移するところを記述します。
1: // Start Condition - 0
begin
i2c_scl = 1;
i2c_sda = 0;
mode_i2c = 2;
end
4)デバイスアドレスの送信(Transmitting Device Address)
スタートコンディション後はデバイスアドレスを送信します。7bitのデバイスアドレスを出力します。
2: // Device Address - scl low
begin
i2c_scl = 0;
mode_i2c = 3;
end
3: // Device Address - set sda
begin
i2c_sda = (i2c_addr & 7'b100_0000) ? 1 : 0;
i2c_addr = i2c_addr << 1;
mode_i2c = 4;
end
4: // Device Address - set scl
begin
i2c_scl = 1;
counter = counter - 1;
if (counter == 0)
mode_i2c = 5;
else
mode_i2c = 2;
end
5)リードライトフラグの出力 (Transmitting Read/Write Flag)
5: //
begin
ic2_scl = 0;
mode_i2c = 6;
end
6:
begin
i2c_sda = rw_flag;
mode_i2c = 7;
end
7:
begin
i2c_scl = 1;
mode_i2c = 8;
end
6)ACKの読み出し
8:
begin
i2c_scl = 0;
mode_i2c = 9;
end
9:
begin
i2c_sda = 1'bz;
mode_i2c = 10;
end
10:
begin
i2c_scl = 1;
mode_i2c = 11;
end
11:
begin
if (i2c_sda == 1)
begin
// Error - go to Stop Sequence
mode_i2c = 250;
end
else
begin
// ACK received - Data Send
mode_i2c = 12;
counter = 8;
end
7) Dataの出力(Transmitting Data)
12:
begin
i2c_scl = 0;
mode_i2c = 13;
end
13:
begin
i2c_sda = (i2c_data & 8'b1000_0000) ? 1 : 0;
i2c_data = i2c_data << 1;
mode_i2c = 14;
end
14:
begin
i2c_scl = 1;
counter = counter -1;
if (counter == 0)
begin
i2c_mode = 15;
end
else
begin
i2c_mode = 12;
end
end
8)ACKの読み出し
15:
begin
i2c_scl = 0;
mode_i2c = 16;
end
16:
begin
i2c_sda = 1'bz;
mode_i2c = 17;
end
17:
begin
i2c_scl = 1;
mode_i2c = 18;
end
19:
begin
if (i2c_sda == 1)
begin
// Error - go to Stop Sequence
mode_i2c = 20;
end
else
begin
// ACK received - Stop Sequence
mode_i2c = 21;
end
9) ストップシーケンス(Stop Sequence)
20:
begin
error_flag = 1;
end
21:
begin
i2c_scl = 0;
mode_i2c = 22;
end
22:
begin
i2c_sda = 0;
mode_i2c = 23;
end
23:
begin
i2c_scl = 1;
mode_i2c = 24;
end
24:
begin
i2c_sda = 1;
mode_i2c = 25;
end
25:
begin
// idle mode
i2c_scl = 1;
i2c_sda = 1;
end
コード例
module i2c_if(clock_200kHz, start_n, rw_flag, addr, data, data_2, i2c_scl, i2c_sda, busy_flag, error_flag, alt_flag);
input clock_200kHz;
input start_n;
input [6:0] addr;
input [7:0] data;
input [7:0] data_2;
reg [7:0] i2c_addr;
reg [7:0] i2c_data;
output i2c_scl;
reg i2c_scl;
inout i2c_sda;
reg i2c_sda;
reg i2c_ack;
output error_flag;
reg error_flag;
output busy_flag;
reg busy_flag;
output alt_flag;
reg alt_flag;
reg [7:0] mode_i2c;
reg [7:0] counter;
input rw_flag;
always@(posedge clock_200kHz or negedge start_n)
begin
if (start_n == 0)
begin
mode_i2c = 0;
error_flag = 0;
busy_flag = 0;
alt_flag = 0;
end
else
begin
case (mode_i2c)
0: // Initial Condition
begin
busy_flag = 1;
i2c_scl = 1;
i2c_sda = 1;
mode_i2c = 1;
alt_flag = 1;
counter = 7;
end
1: // Start Condition - 0
begin
i2c_scl = 1;
i2c_sda = 0;
mode_i2c = 2;
i2c_addr = addr;
end
2: // TRANSMITTING DEVICE ADDRESS - scl low
begin
i2c_scl = 0;
mode_i2c = 3;
end
3: // Device Address - set sda
begin
i2c_sda = (i2c_addr & 7'b100_0000) ? 1 : 0;
i2c_addr = i2c_addr << 1;
mode_i2c = 4;
end
4: // Device Address - set scl
begin
i2c_scl = 1;
counter = counter - 1;
if (counter == 0)
mode_i2c = 5;
else
mode_i2c = 2;
end
5: // TRANSMITTING RW FLAG
begin
i2c_scl = 0;
mode_i2c = 6;
end
6:
begin
i2c_sda = rw_flag;
mode_i2c = 7;
end
7:
begin
i2c_scl = 1;
mode_i2c = 8;
end
8:
begin
i2c_scl = 0;
mode_i2c = 9;
end
9:
begin
i2c_sda = 1'bz;
mode_i2c = 10;
end
10:
begin
i2c_scl = 1;
mode_i2c = 11;
end
11: // checking ACK - transmitting address;
begin
i2c_ack = i2c_sda;
if (i2c_ack == 1)
begin
// Error - go to Stop Sequence
mode_i2c = 27;
end
else
begin
// ACK received - Data Send
mode_i2c = 12;
counter = 8;
i2c_data = data;
alt_flag= 0;
end
end
12: // transmitting data-0
begin
i2c_scl = 0;
mode_i2c = 13;
end
13:
begin
i2c_sda = (i2c_data & 8'b1000_0000) ? 1 : 0;
i2c_data = i2c_data << 1;
mode_i2c = 14;
end
14:
begin
i2c_scl = 1;
counter = counter -1;
if (counter == 0)
begin
mode_i2c = 15;
end
else
begin
mode_i2c = 12;
end
end
15: // checking ACK - transmitting data-0
begin
i2c_scl = 0;
mode_i2c = 16;
end
16:
begin
i2c_sda = 1'bz;
mode_i2c = 17;
end
17:
begin
i2c_scl = 1;
mode_i2c = 18;
end
18:
begin
mode_i2c = 19;
end
19:
begin
i2c_ack = i2c_sda;
if (i2c_ack == 1)
begin
// Error - go to Stop Sequence
mode_i2c = 27;
end
else
begin
// ACK received - Transmitting data-1
mode_i2c = 20;
i2c_data = data_2;
counter = 8;
alt_flag = 1;
end
end
20: // transmitting data-1
begin
i2c_scl = 0;
mode_i2c = 21;
end
21:
begin
i2c_sda = (i2c_data & 8'b1000_0000) ? 1 : 0;
i2c_data = i2c_data << 1;
mode_i2c = 22;
end
22:
begin
i2c_scl = 1;
counter = counter -1;
if (counter == 0)
begin
mode_i2c = 23;
end
else
begin
mode_i2c = 20;
end
end
23: // checking ACK - transmitting data-0
begin
i2c_scl = 0;
mode_i2c = 24;
end
24:
begin
i2c_sda = 1'bz;
mode_i2c = 25;
end
25:
begin
i2c_scl = 1;
mode_i2c = 26;
end
26:
begin
i2c_ack = i2c_sda;
if (i2c_ack == 1)
begin
// Error - go to Stop Sequence
mode_i2c = 27;
end
else
begin
// ACK received - Stop Sequence
mode_i2c = 28;
alt_flag = 0;
end
end
27: // set error flag
begin
error_flag = 1;
mode_i2c = 28;
end
28: // stop condition sequence
begin
i2c_scl = 0;
mode_i2c = 29;
end
29:
begin
i2c_sda = 0;
mode_i2c = 30;
end
30:
begin
i2c_scl = 1;
mode_i2c = 31;
end
31:
begin
i2c_sda = 1;
mode_i2c = 32;
end
32:
begin
// idle mode
i2c_scl = 1;
i2c_sda = 1;
mode_i2c = 33;
end
33:
begin
// clear busy flag
busy_flag = 0;
alt_flag = ~alt_flag;
end
endcase
end
end
endmodule