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

counter

アクセスカウンター