User Tools

Site Tools


Action disabled: revisions
blog:2025-07-23-002



2025-07-23 [FPGA] verilog實作的i2c介面控制

Local Backup

  • I2C 協定規範:
  • 一、時鐘
    • 首先第一步是產生fast-mode的400khz的scl速率,假設方波高低電平各佔一半,即1.25us,理論上不滿足規範上scl低週期1.3us,但是絕大多數裝置都支援稍微超過400khz的速率。
    • 我們仍打算產生一個規範內的速率。輸入時脈clk=20mhz,計數12+1次後翻轉,即可產生一個週期為13x2x50ns=1.3us的方波clk_800,這個週期滿足規格。
    • 也可以改變計數方式,產生一個高低佔比不一樣的波形,可以適當提高速率,同時也符合規範。
    • //     +clk2------------------------------------------
       // 1 )     20mhz -> clk2 -> SCL-400khz clk2==2xSCL
       // 2 ) cnt =     20m/800k = 25 /2 = 12 cnt[3:
       0 ]
               //
           -clk2     ------------------------------------------------
       06月begin 
              cnt_clk <= 0 ; 
              clk_800k <= 0 ;
               end 
          else  if (cnt_clk== 12 )
               begin 
              cnt_clk <= 0 ;
              clk_800k <= ~clk_800k; // scl@falling sda@rising 
              end 
          else 
              cnt_clk <= cnt_clk + 1 ;
  • 二、我們的規劃是,scl在clk_800k的下降沿翻轉,而sda在clk_800k的上升沿變化,這樣就能保證在scl的高電平週期,sda是絕對穩定的,同時也能方便地產生start/stop條件。
    • 根據上面clk的數,可知scl的週期是1.3us x2=2.6us,速率約384khz,沒到400k。
    • //     +iic------------------------------------------
       //     1) 
       //     -iic------------------------------------------ 
      always @( negedge clk_800k or  negedge rst_n) // negedge 
          if (rst_n== 0 )
              iic_scl <= 0 ;
           else 
              iic_scl <= ~iic_scl; // 400k
  • 三、接下來就是採用狀態機控制產生協定要求的時序波形。在clk_800k的rising沿動作。
    • IIC_start:偵測到scl的高,將sda拉低,產生start條件。佔用了1/4個scl週期,0.65us,完全滿足start的建立條件;
    • IIC_data_setup:這個節拍剛好是scl的低週期,不需要判斷,在sda上送出資料的MSB;
    • IIC_data_hold:scl的高週期,sda必須保持不變。循環傳出8個bit,完成一個位元組後跳到下一狀態;
    • IIC_ack_setup:先要釋放sda,讓從裝置去拉低迴應;
    • IIC_ack_hold:判斷回應;如果不回應,或我這裡要求傳完3個byte的數據,跳到下一狀態;
    • IIC_stop_setup:這一步主要是把sda拉低,準備做stop;
    • IIC_stop_hold:在scl高週期,sda拉高,產生stop條件;結束。
  • 只要第一步的start條件切入準了,後面跟著節拍來,就不需要反覆判斷scl的高低了。
    • //     +iic------------------------------------------
       //     1)
       //     -iic------------------------------------------ 
      always @( posedge clk_800k or  negedge rst_n)
           if (rst_n== 0 )
               begin 
              NS_iic <= IIC_idle;
              iic_sda <= 1 ;
              finish_iic <= 0 ;
               end 
          else 
              case (NS_iic)
                  IIC_idle:
                      begin 
                      if (start_iic== 1 )
                          NS_iic <= IIC_start;
                       else 
                          NS_iic <= IIC_idle;
                       end
                  IIC_start:
                      begin 
                      if (iic_scl== 1 )
                           begin 
                          NS_iic <= IIC_data_setup;
                          iic_sda <= 0 ; // start condition 
                          data_iic <= bram_dout[ 23 : 0 ];
                          cnt_byte <= 2 ;
                          cnt_bit <= 0 ; // 0->7 
                          end 
                  //     else 
                   //         NS_iic <= IIC_start; 
                      end 
                  IIC_data_setup: // data setup 
                      begin 
                      NS_iic <= IIC_data_hold;
                      iic_sda <= data_iic[ 23 ];
                      cnt_bit <= cnt_bit - 1 ;
                       end 
                  IIC_data_hold: // data hold 
                      begin 
                      if (cnt_bit== 0 ) NS_iic <= IIC_ack_setup;
                       else NS_iic <= IIC_data_setup;
                      data_iic <= {data_iic[ 22 : 0 ], 1 ' b0}; 
                      end 
                  IIC_ack_setup: // ack prepare 
                      begin 
                      NS_iic <= IIC_ack_hold;
                      iic_sda <= 1 ' bz; 
                      end 
                  IIC_ack_hold: // ack response 
                      begin 
                      if (iic_sda== 1 ||cnt_byte== 0 )
                          NS_iic <= IIC_stop_setup;
                       else 
                          NS_iic <= IIC_data_setup;
                      cnt_byte <= cnt_byte - 1 ;
                       end
                  IIC_stop_setup:
                      begin 
                      NS_iic <= IIC_stop_hold;
                      iic_sda <= 0 ;
                      finish_iic <= 1 ;
                       end
                  IIC_stop_hold:
                      begin 
                      NS_iic <= IIC_idle;
                      iic_sda <= 1 ;
                      finish_iic <= 0 ;
                       end 
                  default :
                       begin 
                      NS_iic <= IIC_idle;
                       end 
              endcase
    • 每次發送3位元組的數據,addr+reg+data。 cnt_byte是計數3個位元組的cnt_bit是計數每個位元組內的bit位元。藍色是相應位。
    • start條件。發送的資料是AA,即10101010,跟計數位元相對應。
    • stop條件。

TAGS

  • 6 person(s) visited this page until now.

blog/2025-07-23-002.txt · Last modified: 2025/07/23 09:03 by jethro