iT邦幫忙

2021 iThome 鐵人賽

DAY 22
1
Software Development

verilog or very lag系列 第 22

【Day22】I2C Master 狀態機的實現

  • 分享至 

  • xImage
  •  

由於上一篇已經介紹過了 SPI 的 Timing Diagram,那麼今天就直接進入 I2C Master 狀態機的程式嘍~~

設計I2C Master的狀態機

先來定義 8 個狀態(下面會解釋各個狀態的行為)

  • IDLE
  • GO
  • START
  • WAIT
  • SHIFT
  • STOP
  • FINAL
  • END
/*---------parameter---------*/
parameter IDLE  = 3'd0;
parameter GO    = 3'd1;
parameter START = 3'd2;
parameter WAIT  = 3'd3;
parameter SHIFT = 3'd4;
parameter STOP  = 3'd5;
parameter FINAL = 3'd6;
parameter END   = 3'd7;

再來定義輸入輸出
輸入:

  • clk_sys
  • rst_n
  • SCLK_100k(輸入已經生成好的 SCLK)
  • en(狀態機 enable)
  • tick_I2C
  • count(計數移位 18 or 27 次的計數器,由外模組來數所以這裡是輸入)
  • R_W(我這次狀態機跑的次數沒有固定,分別為 write 模式的 18 次((8+1)bit x 2),read 模式的 27 次((8+1)bit x 3),低電位是 write,高電位是 read)

輸出:

  • ACK1, ACK2, ACK3(為一時,讓外部模組知道此時要收 ACK)
  • SCLK_temp(負責除了傳輸時以外的 SCLK 訊號控制,會利用 SHEN 判斷搭配 assign 去做切換)
  • LDEN(LoadEnable,告訴外部模塊要 Load 資料)
  • SHEN(ShiftEnable,告訴外部模塊要移位)
  • rstcount(reset count,告訴外部模塊將計數器歸零)
  • countEN(CountEnable,告訴外部模塊將計數器往上計數)
  • SDO(除了資料的 SDA 以外的 SDA 控制,也是會利用 SHEN 判斷搭配 assign 去做切換)
  • rstACK(因為是透過外部暫存器存放 ACK 狀態,因此也需要 reset 這些暫存器的時機,因此需要 rstACK ,會在最後通訊結束時拉到 "1")
  • SCLK(最後總 SCLK 輸出)
module I2C_control_R_W(
  clk_sys, 
  SCLK_100k, 
  tick_I2C, 
  rst_n, 
  en, 
  count, 
  countEN,
  rstcount, 
  ACK1,
  ACK2, 
  ACK3, 
  rstACK, 
  SCLK, 
  SCLK_temp,  
  SHEN, 
  LDEN, 
  SDO, 
  R_W
);
/*---------ports declaration---------*/
input       clk_sys;
input       rst_n;
input       en;
input       SCLK_100k;
input       tick_I2C;
input       R_W;// W---->0, R---->1
input [4:0] count;
output      ACK1; 
output      ACK2; 
output      ACK3; 
output      SCLK_temp; 
output      SHEN; 
output      LDEN; 
output      SDO; 
output      countEN; 
output      rstcount; 
output      rstACK;
output      SCLK;
reg         ACK1; 
reg         ACK2; 
reg         ACK3; 
reg         SCLK_temp; 
reg         SHEN; 
reg         LDEN; 
reg         SDO; 
reg         countEN; 
reg         rstcount; 
reg         rstACK;
wire        SCLK;

宣告跑狀態的變數:

/*---------variables---------*/	
reg [2:0] fstate;

狀態邏輯:

  • 重置時要能回到 IDLE 狀態。
  • IDLE(收到 en(enable)訊號後開始到下一個狀態)
  • GO(只有一個傳輸週期,用來 Load 資料用)
  • START(只有一個傳輸週期,用來 Load 資料用,但這裡發生 START 訊號)
  • WAIT(等待一個傳輸週期)
  • SHIFT(count 數滿 18 or 27 次後並且最後一次也要待滿一個傳輸週期,狀態才往 STOP)
  • STOP(在此狀態會停止所有動作)
  • FINAL(這裡開始要產生 STOP 訊號)
  • END(完成 STOP 訊號的產生,接著再回到 IDLE)
/*---------fstate state---------*/
always@(posedge clk_sys or negedge rst_n)begin
  if(!rst_n)begin
    fstate <= IDLE;
  end
  else begin
    case(fstate)
      IDLE:begin
        if(en) fstate <= GO;
        else   fstate <= IDLE;
      end
      GO:begin
        if(tick_I2C)fstate <= START;
        else        fstate <= GO;
      end
      START:begin
        if(tick_I2C)fstate <= WAIT;
        else        fstate <= START;
      end
      WAIT:begin
        if(tick_I2C)fstate <= SHIFT;
        else        fstate <= WAIT;
      end
      SHIFT:begin
        if(!R_W)begin//write-->18bit
        if(count==5'd16&&tick_I2C)fstate <= STOP;
        else                      fstate <= SHIFT;
      end
      else begin//read-->27bit
        if(count==5'd25&&tick_I2C)fstate <= STOP;
        else                      fstate <= SHIFT;
      end
      end
      STOP:begin
        if(tick_I2C)fstate <= FINAL;
        else        fstate <= STOP;
      end
      FINAL:begin
        if(tick_I2C)fstate <= END;
        else        fstate <= FINAL;
      end
      END:begin
        if(tick_I2C)fstate <= IDLE;
        else        fstate <= END;
      end
    endcase
  end
end

輸出邏輯:

  • IDLE(閒置狀態,SDA 及 SCLK 都為"1")
  • GO(在這裡依舊 SDA 及 SCLK 都為 "1",但會在此狀態 Load 資料)
  • START(這裡發生 START 訊號,SDA 此時為 "0",SCLK 則維持)
  • WAIT(等待一個傳輸週期,SDA 及 SCLK 維持,與上個狀態相同)
  • SHIFT(在此狀態的 SDA 及 SCLK 值不影響,會 assign 到外部模塊,主要做資料的移位以及在第 9、18、27 bit 接收 ACK)
  • STOP(在此狀態 SDA 及 SCLK 都先拉為 "0",停止所有動作)
  • FINAL(這裡開始要產生 STOP 訊號,因此 SCLK 先拉到 "1")
  • END(最後 SDA 拉為 "1",產生 STOP 訊號(完成一次通訊),接著再回到 IDLE)
/*---------fstate output---------*/
always@(posedge clk_sys or negedge rst_n)begin
  if(!rst_n)begin
    SCLK_temp <= 1'b1;
    LDEN      <= 1'b0;
    SHEN      <= 1'b0;
    ACK1      <= 1'b0;
    ACK2      <= 1'b0;
    ACK3      <= 1'b0;
    SDO       <= 1'b1;//SDIN_temp control data[26]/raising/falling
    countEN   <= 1'b0;
    rstcount  <= 1'b0;
    rstACK    <= 1'b0;
  end
  else begin
    if(tick_I2C)begin
      case(fstate)
        IDLE:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b1;//high
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        GO:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b1;//load data
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b1;//high
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        START:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b0;//falling(start)
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        WAIT:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b0;//low
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        SHIFT:begin
          SCLK_temp <= 1'b0;//don't care
          LDEN      <= 1'b0;
          SHEN      <= 1'b1;//shifting
          //ACK1
          if(count==5'd7) ACK1 <= 1'b1;
          else            ACK1 <= 1'b0;
          //ACK2
          if(count==5'd16)ACK2 <= 1'b1;
          else            ACK2 <= 1'b0;
          //ACK3
          if(count==5'd25)ACK3 <= 1'b1;
          else            ACK3 <= 1'b0;
          SDO       <= 1'b1;//don't care(data[26])
          countEN   <= 1'b1;//counting
          //rstcount
          if(!R_W)begin//write-->18bit
            if(count==5'd16)rstcount  <= 1'b1;
            else            rstcount  <= 1'b0;
          end
          else begin//read-->27bit
            if(count==5'd25)rstcount  <= 1'b1;
            else            rstcount  <= 1'b0;
          end
          rstACK    <= 1'b0;
        end
        STOP:begin
          SCLK_temp <= 1'b0;//stop the clock
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b0;//low
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        FINAL:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b0;//low
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b0;
        end
        END:begin
          SCLK_temp <= 1'b1;//high
          LDEN      <= 1'b0;
          SHEN      <= 1'b0;
          ACK1      <= 1'b0;
          ACK2      <= 1'b0;
          ACK3      <= 1'b0;
          SDO       <= 1'b1;//raising
          countEN   <= 1'b0;
          rstcount  <= 1'b0;
          rstACK    <= 1'b1;//reset ACK
        end
      endcase
    end
    else begin
      SCLK_temp <= SCLK_temp;
      LDEN      <= LDEN;
      SHEN      <= SHEN;
      ACK1      <= ACK1;
      ACK2      <= ACK2;
      ACK3      <= ACK3;
      SDO       <= SDO;
      countEN   <= countEN;
      rstcount  <= rstcount;
      rstACK    <= rstACK;
    end
  end
end

endmodule

這次的 I2C Master 看起來雖然好像複雜很多,但其實與前面幾篇的 Uart 及 SPI 都有很大的相似之處歐~~

下一篇我們會繼續來完成 I2C Master 的模組~!


上一篇
【Day21】I2C的介紹
下一篇
【Day23】I2C Master(Write)的實現
系列文
verilog or very lag30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言