1. 程式人生 > >【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十三:DS1302模組

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十三:DS1302模組

實驗二十三:DS1302模組

DS1302這隻硬體雖然曾在《建模篇》介紹過,所以重複的內容請怒筆者懶惰嘮叨了,筆者儘可以一笑帶過,廢話少說讓我們進入正題吧。DS1302是執行事實時鐘(Real Time Clock)的硬體,採用SPI傳輸。

表示23.1 訪問(地址)位元組。

[7]

[6]

[5]

[4]

[3]

[2]

[1]

[0]

1

A5

A4

A3

A2

A1

A0

R/W

DS1302作為從機任由主機蹂躪 ... 啊,是任由主機訪問才對。對此,訪問便有方向之分。如表23.1所示,訪問位元組(地址位元組)[0]為訪問方向1讀0寫。[6..1]為地址。[7]為常量1。除了訪問位元組以外,DS1302也有資料位元組。

clip_image002

圖23.1 寫操作的理想時序(主機視角)。

圖23.1是寫操作的理想時序圖,SCLK為序列時鐘,CS為拉高有效的片選(又名為RESET),DATA是資料進出的I/O。忘了說,DS1302由於使用SPI傳輸的關係,所以下降沿設定資料,上升沿鎖存資料。如圖23.1所示,左邊為訪問位元組,右邊則是資料位元組,CS拉高為寫操作有效。對此,主機先將訪問位元組寫入,再將資料位元組寫入指定的位置。閒置狀態,SCLK訊號還有CS訊號都是拉低發呆,而且讀寫資料都是由低至高。

至於Verilog則可以這樣描述,結果如程式碼23.1所示:

1.    0:
2.    begin { rRST,rSCLK } <= 2'b10; T <= iAddr; i <= FF_Write; Go <= i + 1'
b1; end
3.    1:
4.    begin T <= iData; i <= FF_Write; Go <= i + 1'b1; end
5.    2:
6.    begin { rRST,rSCLK } <= 2'b00; i <= i + 1'b1; end                  
7.    ...
8.    16,17,18,19,20,21,22,23:
9.    begin
10.        isQ = 1'b1;
11.        rSIO <= T[i-16];
12.        if
( C1 == 0 ) rSCLK <= 1'b0;
13.         else if( C1 == FHALF ) rSCLK <= 1'b1;          
14.        if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
15.        else C1 <= C1 + 1'b1;
16.    end                  
17.    24:
18.    i <= Go;

程式碼23.1

步驟0拉高片選,拉低時鐘,準備訪問位元組,然後進入偽函式。步驟1準備資料位元組,然後進入偽函式。步驟2拉低使能,拉低時鐘。

步驟16~23為寫入一個位元組的偽函式,isQ為IO的輸出控制,rSIO為DATA的輸出驅動,rSCLK為SCLK的輸出驅動,FCLK為一個時鐘週期,FHALF為半週期。寫操作只要任由 T 全程驅動 rSIO即可,期間C1為0拉低時鐘,C1為半個週期便拉高時鐘。

步驟24則返回步驟。

clip_image004

圖23.2 寫操作的理想時序(從機視角)。

圖23.2則是從機視角的寫操作時序,從機任何時候都是利用上升沿讀取資料。

clip_image006

圖23.3 讀操作的理想時序(主機視角)。

圖23.3為讀操作的理想時序。T0~T7,主機寫入訪問位元組並且指定讀出地址。T8~T15,從機讀出資料位元組,期間DATA為輸入狀態,從機根據下降沿設定(更新)資料,主機為上升沿讀取。至於Verilog則可以這樣描述,結果如程式碼23.2所示:

1.    0 :
2.    begin { rRST,rSCLK } <= 2'b10; T <= iAddr; i <= FF_Write; Go <= i + 1'b1; end              
3.    1:
4.    begin i <= FF_Read; Go <= i + 1'b1; end
5.    2:
6.    begin { rRST,rSCLK } <= 2'b00; D1 <= T; i <= i + 1'b1; end
7.    ...                  
8.    16,17,18,19,20,21,22,23:
9.    begin
10.         isQ = 1'b1;
11.         rSIO <= T[i-16];
12.         if( C1 == 0 ) rSCLK <= 1'b0;
13.         else if( C1 == FHALF ) rSCLK <= 1'b1;  
14.        if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
15.        else C1 <= C1 + 1'b1;
16.    end                  
17.    24:
18.    i <= Go;              
19.    ...                        
20.    32,33,34,35,36,37,38,39:
21.    begin
22.         isQ = 1'b0;            
23.        if( C1 == 0 ) rSCLK <= 1'b0;
24.         else if( C1 == FHALF ) begin rSCLK <= 1'b1; T[i-32] <= RTC_DATA; end      
25.        if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
26.        else C1 <= C1 + 1'b1;
27.    end
28.    40:
29.    i <= Go;

程式碼23.2

步驟0拉低使能,拉低時鐘,準備訪問位元組,進入偽函式寫。步驟1準備讀資料,進入為函式讀。步驟2為讀取資料,拉低使能,拉低時鐘。步驟16~24為協議一個位元組的偽函式,步驟32~40為讀取一個位元組的偽函式。拉低isQ讓IO為輸入狀態,C1為0拉低 rSCLK,C1為半個週期拉高 rSCLK並且讀取資料,上述操作重複8次便搞定。

clip_image008

圖23.4 讀操作的理想時序(從機視角)。

圖23.4為從機視角的讀操作,從機在T0~T7利用上升沿讀取資料,然後在T8~T15利用下降沿輸出資料。

表23.2 訪問內容/暫存器內容。

訪問地址

暫存器內容

[7]

[6]

[5]

[4]

[3]

[2]

[1]

[0]

範圍

81H

80H

CH

秒十位

秒個位

59~00

83H

82H

分十位

分個位

59~00

85H

84H

12/24

時十位

時個位

12/24~00

87H

86H

日十位

日個位

31~1

89H

88H

月十位

月個位

12~1

8BH

8AH

7~1

8DH

8CH

年十位

年個位

99~00

8FH

8EH

讀保護

C1H

C0H

FFH~00H

...

...

...

FDH

FCH

FFH~00H

表23.2為DS1302的訪問內容,或者說為暫存器地址與暫存器內容,其中秒暫存器的[7]為0開始計時,為1則停止計時。呼叫過程大致如下:

初始化:

l 傳送8EH訪問位元組,關閉防寫;

l 傳送84H訪問位元組,初始化“時種”;

l 傳送82H訪問位元組,初始化“分鐘”;

l 傳送80H訪問位元組,初始化“秒鐘”,開始計時。

呼叫:

l 傳送81H訪問位元組,讀取“秒鐘”內容;

l 傳送83H訪問位元組,讀取“分鐘”內容;

l 傳送85H訪問位元組,讀取“時種”內容;

l 重複上述內容。

至於詳細過程還有具體的暫存器內容,筆者已在《建模篇》解釋過,所以讀者自己看著辦吧。接下來,讓我們進入本實驗的重點內容吧。

表32.3 DS1302的時序引數①。

最小

最大

時序引數

標示

時間

時鐘(50Mhz)

時間

時鐘(50Mhz)

Clock Frequency

FCLK

2Mhz

25

Clock High Time

TCH

250ns

12.5

Clock Low Time

TCL

250ns

12.5

Clock Rise and Fall

TR,TF

0ns

0

500ns

25

首先讓我們先來瞧瞧相關的時序引數。表23.3為速率為2Mhz的時序引數。DS1302最高速率為2Mhz並且無下限,50Mhz的量化結果為25。時鐘訊號拉高TCH或拉低TCL至少需要保持250ns,量化結果為12.5。至於時鐘訊號上山TR或者下山TF最大時間為500ns,極端說是最小時間是0ns。

clip_image010

圖23.5 時序引數①。

如圖23.5所示,那是時鐘訊號還有相關的時序引數,左圖為理想時序,右圖為物理時序。

TR+TH造就前半時鐘週期,TF+TL造就後半時鐘週期,然後TR+TH+TF+TL 為一個時鐘週期。

表23.4 DS1302的時序引數②。

最小

最大

時序引數

標示

時間

時鐘(50Mhz)

時間

時鐘(50Mhz)

CE to Clock Setup

TCC

1us

50

Data to Clock Setup

TDC

50ns

2.5

clip_image012

圖23.6 時序引數②。

如表23.4是TCC還有TDC的時序引數,雖然兩者都有 Setup 字眼,實際上它們都是推擠作用的時序引數。如圖23.6的右圖所示,那是物理時序圖,TCC向右推擠SCLK訊號,TDC向右推擠TDC訊號。換做左圖的理想時序圖,TCC使SCLK被覆蓋,TDC使DATA被覆蓋。有些同學可能會被TCC的要求嚇到,既然是1us。原理上,它是時鐘週期的兩倍,不過那是推擠作用的時序引數,只要操作結束之前一直拉高CS即可無視。

clip_image013表23.4 DS1302的時序引數③。

最小

最大

時序引數

標示

時間

時鐘(50Mhz)

時間

時鐘(50Mhz)

CLOCK to Data Delay

TCDD

200ns

10

Clock to Data Hold

TCDH

70ns

3.5

clip_image015

圖23.7 時序引數③。

表23.4顯示TCDD還有TCDH兩隻時序引數,如23.7的右圖所示,那是物理時序。我們知道DS1302是下降沿設定(輸出)資料,上升沿鎖存資料,期間TCDD將資料向右推擠,TCDH則就是典型的 THold,即鎖存資料以後的確保時間。如23.7的左圖所示,那是理想時序,由於這兩隻都是小傢伙,一般都無法完全覆蓋資料。

clip_image013[1]表23.5 DS1302的時序引數④

最小

最大

時序引數

標示

時間

時鐘(50Mhz)

時間

時鐘(50Mhz)

CE Inactive Time

TCWH

1us

50

CE to I/O High Impedance

TCDZ

70ns

3.5

SCLK to I/O High Impedance

TCCZ

70ns

3.5

表23.5有3個比較奇怪的時序引數,TCWH為片選訊號進入靜態所需的時間,也認為是釋放片選所需的最小時間。至於 TCDZ 與 TCCZ 都是與高阻態有關的時序引數,而高阻態也與I/O息息相關。感覺上,高阻態好比一隻切斷輸出的大刀,而且這隻大刀必須由人操縱,其中TCDZ就是CE切斷輸出狀態所需的最小時間,TCCZ就是SCLK切斷輸出狀態所需的最小時間。

具體而言,如程式碼程式碼23.3所示:

module (...)
    ...
    input Call,
    inout SIO,
    ...
    assign SIO = !Call ? D1 : 1’bz;
endmodule

程式碼23.3

程式碼23.3告訴我們,SIO由 D1 驅動輸出,而且Call拉高便將SIO的輸出切斷。假設TSSZ(Call to I/O High Impedance)是Call切斷輸出所需的時間 ... 細心觀察,Call一旦拉低,SIO並不會立即輸出高阻態,則是必須經歷一段時間。至於高阻態的大小姨媽,一般都是硬體內部(從機)的糾紛,鮮少與驅動方(主機)扯上關係,所以我們在此聊聊就好。

clip_image017

圖23.8 寫操作的物理時序(時序引數)。

clip_image019

圖23.9 讀操作的物理時序(時序引數)。

最後附上兩張官方的原圖23.8還有圖23.9作為本說明的謝幕,讀者就自己慢慢看著辦吧。理解完畢,我們便可以開始建模了。

clip_image021

圖23.10 DS1302基礎模組的建模圖。

圖23.10是DS1302基礎模組的建模圖,內容包含控制模組與功能模組,功能模組負責最基礎的讀寫操作,控制模組則負責功能排程,準備訪問位元組等任務。換之,功能模組的右邊是驅動硬體資源的頂層訊號。其中,功能模組的oData穿過控制模組直接驅動外圍。

ds1302_funcmod.v

clip_image023

圖23.11 DS1302功能模組的建模圖。

圖23.11是DS1302功能模組的建模圖,Call/Done位寬為2,其中 [1]為寫操作,[0]為讀操作。具體內容讓我們來看程式碼吧:

1.    module ds1302_funcmod
2.    (
3.         input CLOCK, RESET,
4.         output RTC_NRST,RTC_SCLK,
5.         inout RTC_DATA,
6.         input [1:0]iCall,
7.         output oDone,
8.         input [7:0]iAddr,iData,
9.         output [7:0]oData
10.    );     

以上內容為相關的出入端宣告。

11.         parameter FCLK = 6'd25, FHALF = 6'd12; // 2Mhz,(1/2Mhz)/(1/50Mhz)
12.         parameter FF_Write = 6'd16, FF_Read = 6'd32;
13.         

以上內容為偽函式入口地址以及常量宣告。FCLK為一個週期,FHALF為半週期。

14.         reg [5:0]C1;
15.         reg [5:0]i,Go;
16.         reg [7:0]D1,T;
17.         reg rRST, rSCLK, rSIO;
18.         reg isQ,isDone;
19.         
20.        always @ ( posedge CLOCK or negedge RESET )    
21.             if( !RESET )
22.                  begin
23.                         C1 <= 6'd0;
24.                         { i,Go } <= { 6'd0,6'd0 };
25.                         { D1,T } <= { 8'd0,8'd0 };
26.                         { rRST, rSCLK, rSIO } <= 3'b000;
27.                         { isQ, isDone } <= 2'b00;
28.                    end

以上內容為相關的暫存器還有復位操作。D1為暫存讀取結果,T為偽函式的操作空間,isQ為 IO的控制輸出。

29.               else if( iCall[1] )
30.                    case( i )
31.                         
32.                          0:
33.                          begin { rRST,rSCLK } <= 2'b10; T <= iAddr; i <= FF_Write; Go <= i + 1'b1; end
34.                          
35.                          1:
36.                          begin T <= iData; i <= FF_Write; Go <= i + 1'b1; end
37.                          
38.                          2:
39.                          begin { rRST,rSCLK } <= 2'b00; i <= i + 1'b1; end
40.                          
41.                          3:
42.                          begin isDone <= 1'b1; i <= i + 1'b1; end
43.                          
44.                          4:
45.                          begin isDone <= 1'b0; i <= 6'd0; end
46.                          
47.                          /******************/
48.                          
49.                          16,17,18,19,20,21,22,23:
50.                          begin
51.                              isQ = 1'b1;
52.                              rSIO <= T[i-16];
53.                                
54.                              if( C1 == 0 ) rSCLK <= 1'b0;
55.                               else if( C1 == FHALF ) rSCLK <= 1'b1;
56.                          
57.                              if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
58.                              else C1 <= C1 + 1'b1;
59.                          end
60.                          
61.                          24:
62.                          i <= Go;
63.                          
64.                     endcase

以上內容為部分核心操作。以上內容是寫操作,步驟16~24是寫一個位元組的偽函式。步驟0拉高片選,準備訪問位元組,並且進入偽函式。步驟1準備寫入資料並且進入偽函式。步驟2拉低片選,步驟3~4則是用來產生完成訊號。

65.            else if( iCall[0] )
66.                    case( i )
67.                     
68.                          0 :
69.                          begin { rRST,rSCLK } <= 2'b10; T <= iAddr; i <= FF_Write; Go <= i + 1'b1; end
70.                          
71.                          1:
72.                          begin i <= FF_Read; Go <= i + 1'b1; end
73.                          
74.                          2:
75.                          begin { rRST,rSCLK } <= 2'b00; D1 <= T; i <= i + 1'b1; end
76.                          
77.                          3:
78.                          begin isDone <= 1'b1; i <= i + 1'b1; end
79.                          
80.                          4:
81.                          begin isDone <= 1'b0; i <= 6'd0; end
82.                          
83.                          /*********************/
84.                          
85.                          16,17,18,19,20,21,22,23:
86.                          begin
87.                              isQ = 1'b1;
88.                              rSIO <= T[i-16];
89.                                
90.                              if( C1 == 0 ) rSCLK <= 1'b0;
91.                               else if( C1 == FHALF ) rSCLK <= 1'b1;
92.                          
93.                              if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
94.                              else C1 <= C1 + 1'b1;
95.                          end
96.                          
97.                          24:
98.                          i <= Go;
99.                          
100.                          /*********************/
101.                            
102.                          32,33,34,35,36,37,38,39:
103.                          begin
104.                              isQ = 1'b0;
105.                                
106.                              if( C1 == 0 ) rSCLK <= 1'b0;
107.                               else if( C1 == FHALF ) begin rSCLK <= 1'b1; T[i-32] <= RTC_DATA; end
108.                          
109.                              if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end
110.                              else C1 <= C1 + 1'b1;
111.                          end
112.                          
113.                          40:
114.                          i <= Go;
115.                          
116.                     endcase
117.            

以上內容為部分核心操作。以上內容是讀操作,步驟16~24是寫一個位元組的偽函式,步驟32~40則是讀一個位元組的偽函式。步驟0拉高使能,準備訪問位元組並且進入寫函式。步驟1進入讀函式。步驟2拉低使能之餘,也將讀取結果暫存至D。步驟3~4用來產生完成訊號。

118.            assign { RTC_NRST,RTC_SCLK } = { rRST,rSCLK };
119.            assign RTC_DATA = isQ ? rSIO : 1'bz;
120.            assign oDone = isDone;
121.            assign oData = D1;
122.    
123.    endmodule

以上內容為相關輸出驅動宣告,其中rSIO驅動RTC_DATA,D驅動oData。

ds1302_ctrlmod.v

clip_image025

圖23.11 DS1302控制模組的建模圖。

圖23.11是該控制模組的建模圖,右邊訊號用來呼叫功能模組,左邊訊號則被呼叫,其中Call/Done 為8位寬,位寬內容如表23.6所示:

表23.6 Call/Done 的位寬內容。

內容

[7]

關閉防寫

[6]

寫入時鐘

[5]

寫入分鐘

[4]

寫入秒鐘

[3]

開啟防寫

[2]

讀取時鐘

[1]

讀取分鐘

[0]

讀取秒鐘

1.    module ds1302_ctrlmod
2.    (
3.         input CLOCK, RESET,     
4.         input [7:0]iCall,
5.         output oDone,
6.         input [7:0]iData,
7.         output [1:0]oCall,
8.         input iDone,
9.         output [7:0]oAddr, oData
10.    );     

以上內容為相關的出入端宣告。

11.         reg [7:0]D1,D2;
12.         
13.         always @ ( posedge CLOCK or negedge RESET )
14.             if( !RESET )
15.                  begin
16.                        D1 <= 8'd0;
17.                         D2 <= 8'd0;
18.                    end
19.              else 
20.                  case( iCall[7:0] )
21.                    
22.                         8'b1000_0000 : // Write unprotect
23.                         begin D1 = 8'h8E; D2 = 8'b0000_0000; end
24.                        
25.                         8'b0100_0000 : // Write hour
26.                         begin D1 = 8'h84; D2 = iData; end
27.                         
28.                         8'b0010_0000 : // Write minit
29.                         begin D1 = 8'h82; D2 = iData; end
30.                         
31.                         8'b0001_0000 : // Write second
32.                         begin D1 = 8'h80; D2 = iData; end
33.                         
34.                         8'b0000_1000 : // Write protect
35.                         begin D1 = 8'h8E; D2 = 8'b1000_0000; end
36.                         
37.                         8'b0000_0100 : // Read hour
38.                         begin D1 = 8'h85; end
39.                         
40.                         8'b0000_0010 : // Read minit
41.                         begin D1 = 8'h83; end
42.                         
43.                         8'b0000_0001 : // Read second  
44.                         begin D1 = 8'h81; end
45.                    
46.                    endcase
47.         

以上內容為準備訪問位元組還有資料節還的周邊操作,它會根據iCall 的內容準備D1與D2。

48.         reg [1:0]i;
49.         reg [1:0]isCall;
50.         reg isDone;
51.         
52.         always @ ( posedge CLOCK or negedge RESET )
53.             if( !RESET )
54.                  begin
55.                         i <= 2'd0;
56.                         isCall <= 2'b00;
57.                         isDone <= 1'b0;
58.                    end

以上內容為相關暫存器宣告還有復位操作。

59.              else if( iCall[7:3] ) // Write action
60.                  case( i )
61.                    
62.                        0 :
63.                         if( iDone ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
64.                         else begin isCall[1] <= 1'b1; end
65.                         
66.                         1 :
67.                         begin isDone <= 1'b1; i <= i + 1'b1; end
68.                         
69.                         2 :
70.                         begin isDone <= 1'b0; i <= 2'd0; end
71.                          
72.                    endcase

以上內容為呼叫寫操作。

73.              else if( iCall[2:0] ) // Read action
74.                  case( i )
75.                    
76.                        0 :
77.                         if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
78.                         else begin isCall[0] <= 1'b1; end
79.                         
80.                         1 :
81.                         begin isDone <= 1'b1; i <= i + 1'b1; end
82.                         
83.                         2 :
84.                         begin isDone <= 1'b0; i <= 2'd0; end
85.                          
86.                    endcase
87.          

以上內容為呼叫讀操作。

88.          assign oDone = isDone;
89.          assign oCall = isCall;
90.          assign oAddr = D1;
91.          assign oData = D2;
92.    
93.    endmodule

以上內容為相關的輸出驅動。

ds1302_basemod.v

該模組的連線部署請參考圖23.10。

1.    module ds1302_basemod
2.    (
3.         input CLOCK, RESET,
4.         output RTC_NRST, RTC_SCLK,
5.         inout RTC_DATA,
6.         input [7:0]iCall,
7.         output oDone,
8.         input [7:0]iData,
9.         output [7:0]oData
10.    );
11.         wire [7:0]AddrU1;
12.         wire [7:0]DataU1;
13.         wire [1:0]CallU1;
14.         
15.         ds1302_ctrlmod U1
16.         (
17.             .CLOCK( CLOCK ),
18.              .RESET( RESET ),
19.              .iCall( iCall ),          // < top
20.              .oDone( oDone ),        // > top
21.              .iData( iData ),        // > top
22.              .oCall( CallU1 ),      // > U2
23.              .iDone( DoneU2 ),        // < U2
24.              .oAddr( AddrU1 ),        // > U2
25.              .oData( DataU1 )         // > U2
26.         );
27.         
28.         wire DoneU2;
29.         
30.         ds1302_funcmod U2
31.         (
32.              .CLOCK( CLOCK ),
33.              .RESET( RESET ),
34.              .RTC_NRST( RTC_NRST ),     // > top
35.              .RTC_SCLK( RTC_SCLK ),     // > top
36.              .RTC_DATA( RTC_DATA ),     // <> top
37.              .iCall( CallU1 ),                // < U1
38.              .oDone( DoneU2 ),            // > U1
39.              .iAddr( AddrU1 ),               // > U1
40.              .iData( DataU1 ),              // > U1
41.              .oData( oData )                // > top
42.         );
43.    
44.    endmodule
ds1302_demo.v

clip_image027

圖23.12 實驗二十三的建模圖。

圖23.12是實驗二十三的基礎模組。核心操作先初始化DS1302基礎模組,然後無間斷從哪裡讀取時鐘,分鐘還有秒鐘,最後驅動至SMG基礎模組。具體內容讓我們來看程式碼吧。

1.    module ds1302_demo
2.    (
3.         input CLOCK, RESET,
4.         output RTC_NRST, RTC_SCLK, 
5.         inout RTC_DATA,
6.         output [7:0]DIG,
7.         output [5:0]SEL
8.    );

以上內容為相關出入端宣告。

9.         wire DoneU1;
10.         wire [7:0]DataU1;
11.         
12.        ds1302_basemod U1
13.        (
14.             .CLOCK( CLOCK ), 
15.             .RESET( RESET ),
16.             .RTC_NRST( RTC_NRST ),      // > top
17.             .RTC_SCLK( RTC_SCLK ),             // > top
18.             .RTC_DATA( RTC_DATA ),          // > top
19.             .iCall( isCall ),         // < core
20.             .oDone( DoneU1 ),        // > core
21.             .iData( D1 ),            // < core
22.             .oData( DataU1 )        // > core
23.        );
24.         

以上內容為DS1302基礎模組的例項化。

25.         smg_basemod U2
26.         (
27.              .CLOCK( CLOCK ),
28.              .RESET( RESET ),
29.              .DIG( DIG ),          // > top
30.              .SEL( SEL ),          // > top
31.              .iData( D2 )          // < core
32.         );

以上內容為SMG基礎模組的例項化。

34.        reg [3:0]i;
35.        reg [7:0]isCall;
36.        reg [7:0]D1;
37.        reg [23:0]D2;
38.        
39.        always @ ( posedge CLOCK or negedge RESET )
40.            if( !RESET )
41.                 begin
42.                      i <= 4'd0;
43.                      isCall <= 8'd0;
44.                      D1 <= 8'd0;
45.                      D2 <= 24'd0;
46.                  end
47.             else 

以上內容為相關暫存器宣告還有復位操作。

48.                 case( i )
49.                  
50.                        0:
51.                        if( DoneU1 ) begin isCall[7] <= 1'b0; i <= i + 1'b1; end
52.                        else begin isCall[7] <= 1'b1; D1 <= 8'h00; end
53.                        
54.                        1:
55.                        if( DoneU1 ) begin isCall[6] <= 1'b0; i <= i + 1'b1; end
56.                        else begin isCall[6] <= 1'b1; D1 <= { 4'd2, 4'd1 }; end
57.                        
58.                        2:
59.                        if( DoneU1 ) begin isCall[5] <= 1'b0; i <= i + 1'b1; end
60.                        else begin isCall[5] <= 1'b1; D1 <= { 4'd5, 4'd9 }; end
61.                        
62.                        3:
63.                        if( DoneU1 ) begin isCall[4] <= 1'b0; i <= i + 1'b1; end
64.                        else begin isCall[4] <= 1'b1; D1 <= { 4'd5, 4'd0 }; end
65.                        
66.                        /*************/
67.                        

以上內容為核心操作的部分內容,步驟0關閉防寫,步驟1初始化時鐘,步驟2初始化分鐘,步驟3初始化秒鐘並且開啟計時。

68.                        4:
69.                        if( DoneU1 ) begin D2[7:0] <= DataU1; isCall[0] <= 1'b0; i <= i + 1'b1; end
70.                        else begin isCall[0] <= 1'b1; end
71.                        
72.                        5:
73.                        if( DoneU1 ) begin D2[15:8] <= DataU1; isCall[1] <= 1'b0; i <= i + 1'b1; end
74.                        else begin isCall[1] <= 1'b1; end
75.                        
76.                        6:
77.                        if( DoneU1 ) begin D2[23:16] <= DataU1; isCall[2] <= 1'b0; i <= 4'd4; end
78.                        else begin isCall[2] <= 1'b1; end
79.                        
80.                  endcase
81.    
82.    endmodule

步驟4讀取秒鐘然後暫存至D2[7:0],步驟5讀取分鐘然後暫存至D2[15:8],步驟6讀取時鐘然後暫存至D2[23:16]。綜合完畢情切下載程式,如果數碼管從21-59-50開始起跑,表示實驗成功。

細節一:完整的個體模組

本實驗的DS1302基礎模組雖然是就緒的完整模組,不過依然無法滿足那些欲無止境的讀者 ... 例如,讀年或者讀天,還是利用DS1302的RAM。對此,讀者請自己探索然後嘗試擴充套件該基礎模組吧。

相關推薦

黑金原創教程FPGA那些事兒-驅動I 實驗流水燈模組

實驗一:流水燈模組 對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低階建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。 圖1.1 實驗一建模圖。 如圖1.1 所示,實驗一有名為 led_funcmod的功能模組。如果無視環境訊號(時鐘訊號還有復位訊號),該功能模組只有

黑金原創教程FPGA那些事兒-驅動I 連載導讀

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組② — 點選與長點選

實驗三:按鍵模組② — 點選與長點選 實驗二我們學過按鍵功能模組的基礎內容,其中我們知道按鍵功能模組有如下操作: l 電平變化檢測; l 過濾抖動; l 產生有效按鍵。 實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵: l 按下有效(點選); l 長按下有效(長點選)。 圖3

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組

實驗二:按鍵模組① - 消抖 按鍵消抖實驗可謂是經典中的經典,按鍵消抖實驗雖曾在《建模篇》出現過,而且還惹來一堆麻煩。事實上,筆者這是在刁難各位同學,好讓對方的慣性思維短路一下,但是慘遭口水攻擊 ... 面對它,筆者宛如被甩的男人,對它又愛又恨。不管怎麼樣,如今 I’ll be back,筆者再也不會重複一

黑金原創教程FPGA那些事兒-驅動I 實驗數碼管模組

實驗六:數碼管模組 有關數碼管的驅動,想必讀者已經學爛了 ... 不過,作為學習的新儀式,再爛的東西也要溫故知新,不然學習就會不健全。黑金開發板上的數碼管資源,由始至終都沒有改變過,筆者因此由身懷念。為了點亮多位數碼管從而顯示數字,一般都會採用動態掃描,然而有關動態掃描的資訊請怒筆者不再重複。在此,同樣也是

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組③ — 單擊與雙擊

實驗四:按鍵模組③ — 單擊與雙擊 實驗三我們建立了“點選”還有“長點選”等有效按鍵的多功能按鍵模組。在此,實驗四同樣也是建立多功能按鍵模組,不過卻有不同的有效按鍵。實驗四的按鍵功能模組有以下兩項有效按鍵: l 單擊(按下有效); l 雙擊(連續按下兩下有效)。 圖4.1 單擊有效按鍵,時序示意圖

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組④ — 點選,長點選,雙擊

實驗五:按鍵模組④ — 點選,長點選,雙擊 實驗二至實驗四,我們一共完成如下有效按鍵: l 點選(按下有效) l 點選(釋放有效) l 長擊(長按下有效) l 雙擊(連續按下有效) 然而,不管哪個實驗都是隻有兩項“功能”的按鍵模組而已,如今我們要建立三項“功能”的按鍵模組,亦即點選(按下有效),長

黑金原創教程FPGA那些事兒-驅動I 原創教程連載導讀連載完成,共十九章

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗十一SDRAM模組④ — 頁讀寫 β

實驗二十一:SDRAM模組④ — 頁讀寫 β 未進入主題之前,讓我們先來談談一些重要的體外話。《整合篇》之際,筆者曾經比擬Verilog如何模仿for迴圈,我們知道for迴圈是順序語言的產物,如果Verilog要實現屬於自己的for迴圈,那麼它要考慮的東西除了步驟以外,還有非常關鍵的時鐘。 for(

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組④ — 普通滑鼠

實驗十:PS/2模組④ — 普通滑鼠 學習PS/2鍵盤以後,接下來就要學習 PS/2 滑鼠。PS/2滑鼠相較PS/2鍵盤,驅動難度稍微高了一點點,因為FPGA(從機)不僅僅是從PS/2滑鼠哪裡讀取資料,FPGA還要往滑鼠裡寫資料 ... 反之,FPGA只要對PS/2鍵盤讀取資料即可。然而,最傷腦筋的地方就在

黑金原創教程FPGA那些事兒-驅動I 實驗十八SDRAM模組① — 單字讀寫

實驗十八:SDRAM模組① — 單字讀寫 筆者與SDRAM有段不短的孽緣,它作為冤魂日夜不斷糾纏筆者。筆者嘗試過許多方法將其退散,不過屢試屢敗的筆者,最終心情像橘子一樣橙。《整合篇》之際,筆者曾經大戰幾回兒,不過內容都是點到即止。最近它破蠱而出,日夜不停:“好~痛苦!好~痛苦!”地呻吟著,嚇得筆者不敢半夜如

黑金原創教程FPGA那些事兒-驅動I 實驗十七TFT模組

實驗二十七:TFT模組 - 顯示 所謂TFT(Thin Film Transistor)就是眾多LCD當中,其中一種支援顏色的LCD,相較古老的點陣LCD(12864笑),它可謂高階了。黑金的TFT LCD除了320×240大小以外,內建SSD1289控制器,同時也是獨立模組。事實上,無論是驅動點陣LCD還

黑金原創教程FPGA那些事兒-驅動I 實驗十三串列埠模組② — 接收

實驗十三:串列埠模組② — 接收 我們在實驗十二實現了串列埠傳送,然而這章實驗則要實現串列埠接收 ... 在此,筆者也會使用其它思路實現串列埠接收。 圖13.1 模組之間的資料傳輸。 假設我們不考慮波特率,而且一幀資料之間的傳輸也只是發生在FPGA之間,即兩隻模組之間互轉,並且兩塊模組都使用相同的時

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組① — 鍵盤

實驗七:PS/2模組① — 鍵盤 實驗七依然也是熟爛的PS/2鍵盤。相較《建模篇》的PS/2鍵盤實驗,實驗七實除了實現基本的驅動以外,我們還要深入解PS/2時序,還有PS/2鍵盤的行為。不過,為了節省珍貴的頁數,怒筆者不再重複有關PS/2的基礎內容,那些不曉得的讀者請複習《建模篇》或者自行谷歌一下。 市場

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組③ — 鍵盤與多組合鍵

實驗九:PS/2模組③ — 鍵盤與多組合鍵 筆者曾經說過,通碼除了單位元組以外,也有雙位元組通碼,而且雙位元組通碼都是 8’hE0開頭,別名又是 E0按鍵。常見的的E0按鍵有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC>

黑金原創教程FPGA那些事兒-驅動I 實驗十五SDHC模組

實驗二十五:SDHC模組 筆者曾經說過,SD卡發展至今已經衍生許多版本,實驗二十四就是針對版本SDV1.×的SD卡。實驗二十四也說過,CMD24還有CMD17會故意偏移地址29,讓原本範圍指向從原本的232 變成 223,原因是SD卡讀寫一次都有512個位元組。為此我們可以這樣計算: SDV1.x = 2

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組③ — 頁讀寫 α

實驗二十:SDRAM模組③ — 頁讀寫 α 完成單字讀寫與多字讀寫以後,接下來我們要實驗頁讀寫。醜話當前,實驗二十的頁讀寫只是實驗性質的東西,其中不存在任何實用價值,筆者希望讀者可以把它當成頁讀寫的熱身運動。 表示20.1 Mode Register的內容。 Mode Register

黑金原創教程FPGA那些事兒-驅動I 實驗十八TFT模組

實驗二十八:TFT模組 - 觸屏 讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組⑤ — FIFO讀寫

經過漫長的戰鬥以後,我們終於來到最後。對於普通人而言,頁讀寫就是一名戰士的墓碑(最終戰役) ... 然而,怕死的筆者想透過這個實驗告訴讀者,旅程的終點就是旅程的起點。一直以來,筆者都在煩惱“SDRAM是否應該成為儲存類?”SDRAM作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規

黑金原創教程FPGA那些事兒-驅動I 實驗十四儲存模組

實驗十四比起動手筆者更加註重原理,因為實驗十四要討論的東西,不是其它而是低階建模II之一的模組類,即儲存模組。接觸順序語言之際,“儲存”不禁讓人聯想到變數或者陣列,結果它們好比資料的暫存空間。 1. int main() 2. { 3. int VarA; 4.