IT技術互動交流平臺

FPGA實現串口與iic控制器總結(3)

來源:IT165收集  發布日期:2016-05-03 22:37:14

在剖析了《深入淺出玩轉FPGA》的串口代碼和IIC控制器代碼、xilinx官方的xilinx的iic控制器(參見書《FPGACPLD設計工具──Xilinx ISE使用詳解》)、《片上系統設計思想與源代碼分析》一書中帶有wishbone接口的iic控制器后,本文嘗試對以上做一些總結,并分析不同的iic控制器的實現區別。

上一講,我們分析了特權的iic控制器的實現,這一講將繼續另2種帶有總線接口和更充分實現iic協議的例子。下一講講談談SOC架構中的wishbone總線架構及外設掛載,以及一些軟硬件結合的底層的東西,主要參考《自己動手寫cpu》一書及相關的網頁資料等,敬請期待。

 

2、IIC控制器

2.2 xilinx的iic控制器

該控制器主要是參考《FPGACPLD設計工具──Xilinx ISE使用詳解》第10章和官方代碼,資料鏈接見文末給出的下載鏈接地址。

iic協議的知識同樣不再贅述,只是提幾點上一講中沒有或無需關注的地方:

1、I2C總線通信時,起始位后的第一個字節用于尋址,該字節包含7比特的從機地址和1比特的讀寫指示比特, 一般而言,從機地址由一個固定部分和一個可編程部分構成。

2、復合格式中,此時主機連續對從機進行多次讀寫操作,因此在產生起始位、收發數據、產生停止位的整個傳輸過程中,數據的方向發生多次改變,傳輸改變方向時,主機會重新發出重復起始位和從機地址(上一講中為什么讀的流程要復雜些的原因)。

3、I2C總線在一個時刻只能有一個主機,當I2C總線同時有兩個或更多的器件想成為主機時,就需要進行仲裁,時鐘同步過程的目的是為仲裁提供一個確定的時鐘。SCL 線低電平時間取決于低電平時間最長的主機,高電平時間取決于高電平時間最短的主機。主機只會在 I2C 總線空閑時產生起始位,但是在起始位的保持時間 tHD;STA內可能有兩個或以上的主機產生起始位,最終總線上的起始位由它們之間的線與運算決定,仲裁在隨后 SDA 線上發送的比特中進行。如果一個主機具有從機功能,那么當它失去仲裁時,必須立即切換到從機狀態,因為它可能正在被其他主機尋址。

4、如圖

講完了這些關于iic協議的點繼續下面的部分.

I2C總線控制器的主要作用是提供UC(Microcontroller,微控制器或單片機)和I2C總線之間的接口,為兩者之間的通信提供物理層協議的轉換。這些I2C協議的器件,就不能直接和單片機外圍總線相連,這些器件可以掛在一套I2C總線上,再通過I2C總線控制器和µC連起來,如圖所示。在SOC設計中類似的協議轉換模塊用得非常多。

I2C 總線控制器包含兩個主要部分,一是微控制器接口,簡稱µC接口,二是 I2C Master/Slave 接口,即I2C接口,通過這兩個接口,I2C總線控制器實現了微控制器外圍總線和 I2C 總線的連接。那么這里的微控制器接口是哪種控制器?是否是哪種總線協議,根據readme文件可以發現原來是 MC68307單片機,是一款集成的多總線處理器。

講清楚了原理,我們來看看具體設計:

This zip file contains the following folders:
-- Verilog Source Files:
i2c_blk_ver.v - top level file
i2c_control_blk_ver.v - control function for the I2C master/slave
shift8_blk_ver.v - shift register
uc_interface_blk_ver.v - uC interface function for an 8-bit 68000-like uC
upcnt4_blk_ver.v - 4-bit up counter

可以看到5個文件。uc_interface_blk_ver.v 實現了一些寄存器以及對下層模塊的控制信號,shift8_blk_ver.v是串行收發,upcnt4_blk_ver.v是4位計數器,i2c_blk_ver.v 是頂層,例化了uc_interface_blk_ver和i2c_control_blk_ver。i2c_control_blk_ver則實現了仲裁,start等收發流程狀態機等。整體架構如下圖:

整個思路還是很清楚的。接口處主要是一些寄存器,寄存器的信號到iic中控制底層的運轉。

µC 接口主要包含狀態寄存器MBSR、控制寄存器MBCR、地址寄存器MADR、數據寄存器MBDR 和地址譯碼/總線接口模塊。狀態寄存器指示I2C總線控制器的當前狀態,如傳送是否完成、總線是否忙等信息,控制寄存器是µC控制I2C總線控制器的主要途徑,通過置0/置1可以完成I2C總線控制器使能、中斷使能、Master/Slave模式選擇、產生起始位等操作。地址寄存器保存著I2C總線控制器作為Slave時的地址。數據寄存器用于保存接收或是待發送的數據。

下面分析源代碼,由于源代碼眾多,只能摘出部分,梳理主要思路和重要信號線,大家可以下載我注釋的代碼。

i2c_blk_ver.v 頂層文件:

 

module i2c_blk (sda, scl, addr_bus, data_bus, as, ds, r_w, dtack, irq, mcf, clk,
                reset);
 
  parameter I2C_ADDRESS = 16'b0000000000000000;
  //   I2C bus signals
  inout sda;
  inout scl;
  //   uC interface signals
  input [23:0] addr_bus;
  inout [7:0] data_bus;
  input as;                //   address strobe, active low
  input ds;                //   data strobe, active low
  input r_w;               //   read/write
  output dtack;            //   data transfer acknowledge 給處理器的,表示數據是否準備好了
  output irq;              //   interrupt request
  inout mcf;               //   temporary output for testing  給處理器,表示傳輸是否結束
  //   clock and reset
  input clk;
  input reset;
這個是頂層的輸入輸出,不是什么特殊的總線結構,比較好理解,pdf中有詳細的中文解釋每個信號的含義。

 

接下來是一些wire型,實際上就是那些寄存器中有含義的每一位,即各種控制信號線,便于例化的 i2c_control I2C_CTRL與 uC_interface #(I2C_ADDRESS) uC_CTRL模塊間的接口互聯。

接下來從頂層往下,uC_interface模塊:

 

 //   Internal I2C Bus Registers
  //   Address Register (Contains slave address)
  inout [7:0] madr;
  //   Control Register		
  inout men;               //   I2C Enable bit
  inout mien;              //   interrupt enable
  inout msta;              //   Master/Slave bit
  inout mtx;               //   Master read/write
  inout txak;              //   acknowledge bit
  inout rsta;              //   repeated start
  output mbcr_wr;          //   indicates that the control reg has been written
  //   Status Register
  input mcf;               //   end of data transfer
  input maas;              //   addressed as slave
  input mbb;               //   bus busy
  input mal;               //   arbitration lost
  input srw;               //   slave read/write
  input mif;               //   interrupt pending
  input rxak;              //   received acknowledge
  output mal_bit_reset;    //   indicates that the MAL bit should be reset
  output mif_bit_reset;    //   indicates that the MIF bit should be reset
  input msta_rst;          //   resets the MSTA bit if arbitration is lost
  //   Data Register
  inout [7:0] mbdr_micro;
  input [7:0] mbdr_i2c;
  output mbdr_read;
可以看到實際上這個模塊的很多輸出信號就是這些寄存器的位,控制更底層的如何實現?梢詤⒖紁df查詢具體的寄存器(8位)的某一位是什么含義。比如狀態寄存器都是input型,因為他主要反饋給uc當前狀態,不對底層的狀態機控制。數據寄存器中mbdr_micro是inout型,表示是uc總線那一端的,mbdr_i2c是input型,表示是從iic接收到的那一端的。

 

 

  //   State Machine Signals
  `define STATE_TYPE_IDLE	 2'd0
  `define STATE_TYPE_ADDR	 2'd1
  `define STATE_TYPE_DATA_TRS	 2'd2
  `define STATE_TYPE_ASSERT_DTACK	 2'd3
  //   Constant Declarations
  parameter RESET_ACTIVE = 1'b0;
  //   Base Address for I2C Module (addr_bus[23:8])
  parameter MBASE = UC_ADDRESS;
  //   Register Addresses (5 Total):
  //   Address Register (MBASE + 8Dh)
  `define MADR_ADDR	 8'b10001101
  //   Control Register (MBASE + 91h)
  `define MBCR_ADDR	 8'b10010001
  //   Status Register (MBASE + 93h)
  `define MBSR_ADDR	 8'b10010011
  //   Data I/O Register (MBASE + 95h)
  `define MBDR_ADDR	 8'b10010101
uc部分的狀態機,µC和 I2C總線控制器之間的交互要用到 I2C總線控制器內部的寄存器,寄存器的地址是24位的,其中高16比特為I2C總線控制器的基址,低8位用于區別不同寄存器。接下來定義了10多個wire型的中間變量,這些事狀態機中產生的操縱控制狀態機的。由于wire型變量不能再always中賦值,所以后面又用這種方式定義一個相應的reg型變量。

 

 

//   Address match
  wire address_match;
 reg visual_0_address_match;
  assign address_match = visual_0_address_match;

加上了visual_0的前綴,這樣做的好處?我想一是時鐘同步,reg信號由clock打一拍同步,也便于去除毛刺,因為assign信號直接相連,稍有抖動就有毛刺,reg信號打拍子可以避免這種問題。二是可以某些信號做類似案件檢測一樣的消抖,舉例:

 

51    input as; 
221   begin
      visual_0_as_int 						//這里又有一個時鐘的延遲一拍
      visual_0_as_int_d1					//經過一個時鐘的延遲一拍,為了檢測到正確的下降沿而沒有抖動的干擾
      visual_0_ds_int <= ds;
      if ((!as && as_int_d1 && addr_bus[23:8] == MBASE))		//低8位是區別那個寄存器
        visual_0_address_match <= 1'b1;
      else
        visual_0_address_match <= 1'b0;
      end
135   reg visual_0_as_int;
      assign as_int = visual_0_as_int;
138   reg visual_0_as_int_d1;
      assign as_int_d1 = visual_0_as_int_d1;

 

 

可以看見從221行開始,獲取as輸入,經過幾個中間變量的周轉,在if中通過!as && as_int_d1來達到類似案件去抖的效果。另外有些變量是對外輸出的,也需變成reg型。 這部分的實際工作流程如圖:
根據上圖的流程:第一個always塊, // Process:SYNCH_INPUTS 判斷as是否有有效的下降沿,然后總線的高16位是否是正確的我們這個iic設備在真個cpu外設總線上的地址。為真,則visual_0_address_match信號為1,起始對應為address_match,這個成為后續的控制判斷,狀態機的重要依據。接下來是uc模塊的主狀態機:

 

  always @(prs_state or as or as_int_d1 or ds_int or address_match)
  begin
    visual_0_next_state <= prs_state;			//2位的變量,4個狀態
    visual_0_dtack_com <= 1'b1;					
    visual_0_dtack_oe <= 1'b0;
    case (prs_state)
      `STATE_TYPE_IDLE :						//引用定義的變量
        //  ----------- IDLE State (00) -------------
        //   Wait for falling edge of as
        if (as_int_d1 && !as)					//as表示輸入地址有效信號,低有效,這里的as_int_d1實際上是一個類似按鍵去抖的效果,延遲了2拍,追蹤信號就可以發現
          //   falling edge of AS
          visual_0_next_state <= `STATE_TYPE_ADDR;
 
      `STATE_TYPE_ADDR :
        //  ---------- ADDR State (01) --------------
        //   Check that this module is being address
        if (address_match)						//225行由輸入的bus決定,也是打了一拍,做到時鐘同步
          //   Wait for ds to be asserted, active low
          if (!ds_int)							//由輸入的ds決定
            visual_0_next_state <= `STATE_TYPE_DATA_TRS;
          else
            visual_0_next_state <= `STATE_TYPE_ADDR;
        else
          //   this module is not being addressed
          visual_0_next_state <= `STATE_TYPE_IDLE; 
      `STATE_TYPE_DATA_TRS :
      begin
        //  -------- DATA_TRS State (10) ------------
        //   Read or write from enabled register
        visual_0_next_state <= `STATE_TYPE_ASSERT_DTACK;	//過渡態,為了給出oe的信號,內部控制線
        visual_0_dtack_oe <= 1'b1;							//前面被置為0了的
      end
      `STATE_TYPE_ASSERT_DTACK :
      begin
        //  ------ ASSERT_DTACK State (11) ----------
        //   Assert dtack to uProcessor
        visual_0_dtack_com <= 1'b0;							//前面被置為1了的
        visual_0_dtack_oe <= 1'b1;
        //   Wait for rising edge of as and ds
        if ((!as_int_d1) && (!ds_int))						//鎖存操作,直至DTACK有效,甚至可以分析鎖存了幾個時鐘?
          visual_0_next_state <= `STATE_TYPE_ASSERT_DTACK;
        else if ((as_int_d1) && (ds_int))
          visual_0_next_state <= `STATE_TYPE_IDLE;
      end
    endcase
  end
</pre><pre code_snippet_id="1669606" snippet_file_name="blog_20160502_10_3192142" name="code" class="plain">注意在848行處://   set SDA and SCL
  assign sda = (sda_oe == 1'b1 ? 1'b0 : 1'bz);
  assign scl = (scl_out_reg == 1'b0 ? 1'b0 : 1'bz);
  assign scl_not =  (~ (scl)) ;
  //   sda_oe is set when master and arbitration is not lost and data to be output = 0 or
  //   when slave and data to be output is 0
  assign sda_oe = (((master_slave == 1'b1 && arb_lost == 1'b0 && sda_out_reg ==
                  1'b0) || (master_slave == 1'b0 && slave_sda == 1'b0) ||
                  stop_scl_reg == 1'b1) ? 1'b1 : 1'b0);
所以sda實際上經由sda_out_reg,visual_0_sda_out_reg,sda_out,visual_0_sda_out控制,即下圖狀態機中的visual_0_sda_out實際代表了sda,同樣scl也一樣。

 

 

 

該狀態機還是比較清晰的。

next_state信號通過幾個中間變量的周轉,還是賦給了prs_state實現狀態跳轉,292的always塊即實現了這種從next_state到prs_state的驅動。dtack_com是一個狀態機變化的中間信號,轉化成dtack_int變成dtack信號,dtack_oe決定是否輸出dtack信號。注意這3條語句每次進來就更新,要注意visual_0_dtack_com與visual_0_dtack_oe這里每次被重置。首先是idle狀態,檢測到有效的as信號。進入addr狀態,監測地址是否匹配,數據是否來了,來了就進入trs,否則繼續等。Trs狀態是過渡態,為了給出visual_0_dtack_oe信號,進入dtack狀態,visual_0_dtack_com與visual_0_dtack_oe被設置表明數據已經放到uc與iic的總線上。等待ds失效,回到idle狀態。那么更多的過程實際上是根據狀態機的中間變量,在別的always塊中實現的,可以分析下兩者間的相對時間關系。

接下來的always實現visual_0_prs_state <= next_state;驅動狀態更迭。接下來是判斷那個24位的地址是映射到那個寄存器,每個寄存器給出一個信號線,若選中則為1,驅動下一個always塊中的數據具體傳到哪個寄存器中。下個always塊中,實現了對不同狀態下的一些信號量的設置,而沒有在之前的狀態機中實現。每根信號線的含義可以查pdf。mbcr_wr代表了mbcr是被讀了還是被寫了。其實Status Register是只讀的,寫的話會產生復位。其他幾個寄存器類似,比較好理解,理解關于數據寄存器的數據流向。這里面的8位數據都是一個clk完成,注意與上面的dtack的含義在時序上是否沖突,因為dtack是表征數據已經準備好

最后就是幾個assign語句,根據上面的信號處理是否有中斷,dtack,決定是傳輸進來某個信號還是給出某個信號。

總的來說結構比較清晰,模塊化比較清晰。狀態機采用了分段式的寫法。

i2c_control_blk_ver.v文件

它的接口主要是uc接口文件中的寄存器的信號線。分別定義了數據流程的狀態機和scl信號的狀態機。接下來是一堆的中間信號的定義和類似的加上visual_0后的reg處理。

接下來首先例化了upcnt4模塊用來統計bit數。這是一個不封頂的4位計數器,但是可以輸入一個4位數據來修改計數值。接下來再例化一個用于clk的計數,達到分頻的效果。接下來是例化了2個SHIFT8_blk,改模塊可以load和輸出1個8位數,也可串行接收和發送一個一位數。例化了2個第一個是iic端的,一個是uc端的。接下來是總線仲裁,主要是msta_rst與arb_lost位。當為主機,scl_in為scl的采樣。有待重點分析?

接下來是scl信號的一個狀態機:

I2C 總線控制器復位后處于IDLE 狀態,不驅動SCL 和SDA,此時I2C 總線上的其他Master 可以控制SCL 和SDA。如果I2C 總線控制器處于Master 模式,而且I2C 總線處于空閑狀態,μC 通過置位MBCR 寄存器的MSTA 比特使GEN_START 信號為高,那么狀態機進入START 狀態。

在 START 狀態,SCL 保持為高電平,同時驅動SDA 信號變低,從而在I2C 總線上產生一個起始位。系統時鐘計數器啟動計數,直到滿足I2C 規范要求的起始條件保持時間(>4ns),狀態機進入SCL_LOW_EDGE狀態。

在 SCL_LOW_EDGE 狀態,狀態機使SCL 產生一個下降沿并復位系統時鐘計數器,然后在下一個時鐘沿到來時進入SCL_LOW 狀態。

在SCL_LOW 狀態,SCL 保持為低,同時進行計數,直到產生規定的SCL 低電平時間(>4.7ns)。產生規定的 SCL 低電平時間后,如果失去仲裁,那么完成一個字節的傳輸之后狀態機回到IDLE 狀態,否則狀態機進入SCL_HI_EDGE 狀態。

在 SCL_HI_EDGE 狀態中,狀態機釋放SCL 線,希望產生SCL 上升沿,但是SCL 線可能被其他Master 置低,因此狀態機并不直接轉移到SCL_HI 狀態,而是等待SCL 信號變高之后才進入SCL_HI 狀態。

進入 SCL_HI 狀態后,系統時鐘計數器進行計數,以產生I2C規范要求的SCL高電平時間(>4.0ns),如果檢測到重復起始條件或停止條件,狀態機將在1/2SCL 高電平時間之后轉移到 START狀態重新開始,或轉移到 IDLE狀態,否則產生要求的 SCL高電平時間后狀態機進入 SCL_LOW_EDGE狀態,繼續產生下一個 SCL脈沖。

接下來是狀態機的驅動visual_0_scl_state <=next_scl_state;接下來是visual_0_sda_in這類信號,這個處理,visual_0_scl_in并不是由scl扇出,僅僅是對這個信號的一個采樣。也是上面我們判斷scl是否被別人拉高的依據。

后面是start和stop信號產生和檢測的一個信號線的處理,也包括主從機的。在下來就是主狀態機了:

 

復位后,狀態機在 IDLE 狀態,當檢測到 START 信號時,轉移到HEADER 狀態。START信號由 I2C 總線上的起始位觸發,觸發這個起始位的 Master 可以是 I2C 總線控制器本身或其他的 I2C 總線主機。

在 HEADER 狀態,如果 I2C 總線控制器處于 Master 模式,它會把 MBDR 中的數據作為HEADER 發送到 I2C 總線上,以尋址特定的 Slave。不管 I2C 總線控制器處于 Master 還是Slave模式,在HEADER 狀態時,I2C 總線控制器都會接收總線上的數據,保存到 I2C Header Shift Register 中,收到8 個比特后,狀態機轉移至 ACK_HEADER 狀態。

在 ACK_HEADER 狀態,如果 I2C 總線控制器處于 Master 模式,它會采樣 SDA 線,以判 斷 所 尋 址 的Slave 是 否 響 應 。 如 果 沒 有 響 應 , 狀 態 機 轉 移 到STOP 狀 態 , 通 知SCL/START/STOPGenerator 產生STOP 信號,中止傳輸。如果 Slave 產生了響應比特,狀態機根據 Header 的最低位判斷發起的是發送操作還是接收操作,然后轉移到 RCV_DATA 狀態或 XMIT_DATA 狀態。如果I2C 控制器處于Slave 模式,電路會不斷比較 Header Shift Register 的內容和 I2C 總線控制器地址寄存器 MADR 的內容是否相等,如果相等,說明本I2C總線控制器被其他 Master 尋址,于是 I2C 總線控制器立即轉換到 Slave 模式,并把狀態寄存器 MBSR 的 MAAS 比特置 1,指示 I2C 總線控制器被其他 Master 尋址。同時MBSR 的SRW 比特記錄Header 的最低位,以便µC 判斷Master 請求的是讀還是寫操作。

在 RCV_DATA 狀態, I2C 總線控制器處于接收狀態(即主機接收狀態或從機接收狀態),狀態機讀入 I2C 總線上的數據并保存到移位寄存器中,讀完 8 比特的數據后進入ACK_DATA 狀態,發出響應比特。響應比特的取值根據 I2C 總線控制器是 Master 還是 Slave有所不同,當I2C 總線控制器是Slave 時,響應比特應為 0,表示正常接收;當 I2C 總線控制器是Master 時,如果已經收到了足夠的數據,響應比特要設置為 1,通知 Slave 停止發送,否則響應比特應為 0,通知Slave 繼續發送。響應比特的值由 MBCR 的 TXAK 位決定, µC 可以在適當的時候寫入。檢測到 I2C 總線上的停止位時,狀態機轉移到 STOP 狀態。

在 XMIT_DATA 狀態, I2C 總線控制器處于發送狀態(即主機發送,或從機發送狀態),狀態機把數據寄存器 MBDR 的數據移位輸出到 SDA 線上,發送 8 比特后進入GET_ACK_DATA 狀態,收到響應比特后,狀態機回到 XMIT_DATA 狀態,繼續發送下一個字節。如果沒有收到響應比特,說明發送結束或出錯,狀態機轉到 STOP 狀態。

在 STOP 狀態,如果處于 Master 模式,主狀態機通知 SCL/START/STOP Generator 產生停止位。下一個時鐘沿到來時,狀態機自動轉移到 IDLE 狀態。

后面就是start stop檢測模塊,還有一些寄存器信號線的產生。

可以看到這種設計思路是分層次模塊的。uc那一端是根據數據交互的流程,讀寫控制寄存器,然后寄存器的信號輸出給底層去實現各種控制。而底層也是分區塊,2個狀態機,一個實現scl信號的置高和置低,期間有仲裁檢測,主從機的切換,結束起始位的判斷等等,另一個實現iic與外部設備通信時的交互流程,如先尋址,在等待確認,再續寫之類的。中間有很多信號線去同步控制別的always塊或者被別的信號線控制。還有起始,結束檢測的always塊,針對各個寄存器的賦值的專門的always塊等等。

2.3 OR1200的iic控制器

這是一個帶wishbone總線結構的iic接口,所以按照頂層框圖來畫輸入輸出信號,可以發現輸入一段全是wb接口的信號線,輸出就是2根scl和sda,但是注意的是這里并不是scl與sda:

 

	// I2C signals
	// i2c clock line
	input  scl_pad_i;       // SCL-line input			//?不是inout結構?
	output scl_pad_o;       // SCL-line output (always 1'b0)
	output scl_padoen_o;    // SCL-line output enable (active low)

	// i2c data line
	input  sda_pad_i;       // SDA-line input
	output sda_pad_o;       // SDA-line output (always 1'b0)
	output sda_padoen_o;    // SDA-line output enable (active low)
實際上我們需要在這個三態門添加更高的設計層次:

 

 

	assign scl = scl_padoen_o ? 1'bz : scl_pad_o;
	assign sda = sda_padoen_o ? 1'bz : sda_pad_o;
	assign scl_pad_i = scl;
	assign sda_pad_i = sda;

 

定義了不同的寄存器,包括時鐘分頻的寄存器,支持異步復位。wb_adr_i信號決定具體對哪一個寄存器操作。然后是寄存器的值的修改。這個top模塊中沒有定義狀態機,是由于wb接口的原因,不想2.2的實現方式是有這么一個流程的。而這里因為是有統一的wb接口的原因,所以簡單的多。另外wb接口的很多信號線可以根據自己的需求去修改其功能含義的。

但是這個僅僅是主機,并沒有實現完整的iic接口,沒有仲裁等。

top中例化了i2c_master_byte_ctrl,i2c_master_byte_ctrl中例化了更低層次的i2c_master_bit_ctrl。i2c_master_byte_ctrl中也是一個主狀態機維持著start、發、收、ack等交互流程,i2c_master_bit_ctrl將start、stop、rd、wr都分成了好幾段,維持著一個龐大的狀態機 ,里面是對sda,scl信號線的高低的控制。也是模塊化的比較清晰,就不仔細講了

說幾點感受吧:

1、對于這種比較復雜的設計,實際上模塊的劃分非常重要,理清楚各個模塊間的互聯,每根信號線的含義。比如這里uc那一端的分段式狀態機,與寄存器的設計等。

2、該例程比較復雜,非常繁雜,有很多的信號線。各信號線之間的連接比較繁雜,特別是還有仲裁這一塊,不好理解。所有對于這種復雜的設計,沒有詳細的注釋很難理解。另外針對iic的協議,大家的理解和設計思路不一樣,可以有很多種實現方式,包括一些個人的寫法習慣,如對于狀態,對中間信號的定義與處理等,都有差異

3、很多的小技巧,對于信號的命名,打節拍的處理等等

Tag標簽: 串口   控制器  
  • 專題推薦

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
千宇彩票官网 ye4| uay| s4a| oge| 4em| ci4| gga| q2u| cai| 2wq| kaw| iqe| 3os| us3| iig| c3c| yym| 3wk| ka3| qao| a2o| sqe| 2ci| gw2| ia2| sak| g2a| cwc| 2qw| cs3| wcs| c1y| wek| 1ge| sa1| kwa| s1e| m1a| qqg| q2i| ggm| 2ay| qy2| gao| y0i| csy| 0sg| qe1| sig| m1u| y1c| gwm| 1ag| qi1| kci| g9e| skq| 0ym| eu0| aay| c0y| mec| 0kq| 0cg| sk0| ckw| e0q| sig| q9c| ke3| uec| iym| 9ec| qo9| ygs| i9k| mua| ygm| 0iu| wo8| euk| k8c| skq| 8kw| mm8| cca| o9q| aqe| 9gt| bmd|