【黑金原创教程】【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也有数据字节。

图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则返回步骤。

图23.2 写操作的理想时序(从机视角)。

图23.2则是从机视角的写操作时序,从机任何时候都是利用上升沿读取数据。

图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次便搞定。

图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。

图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

   

图23.6 时序参数②。

如表23.4是TCC还有TDC的时序参数,虽然两者都有 Setup 字眼,实际上它们都是推挤作用的时序参数。如图23.6的右图所示,那是物理时序图,TCC向右推挤SCLK信号,TDC向右推挤TDC信号。换做左图的理想时序图,TCC使SCLK被覆盖,TDC使DATA被覆盖。有些同学可能会被TCC的要求吓到,既然是1us。原理上,它是时钟周期的两倍,不过那是推挤作用的时序参数,只要操作结束之前一直拉高CS即可无视。

表23.4 DS1302的时序参数③。

   
最小


最大


时序参数


标示


时间


时钟(50Mhz)


时间


时钟(50Mhz)


CLOCK to Data Delay


TCDD


200ns


10

   

Clock to Data Hold


TCDH


70ns


3.5

   

图23.7 时序参数③。

表23.4显示TCDD还有TCDH两只时序参数,如23.7的右图所示,那是物理时序。我们知道DS1302是下降沿设置(输出)数据,上升沿锁存数据,期间TCDD将数据向右推挤,TCDH则就是典型的 THold,即锁存数据以后的确保时间。如23.7的左图所示,那是理想时序,由于这两只都是小家伙,一般都无法完全覆盖数据。

表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并不会立即输出高阻态,则是必须经历一段时间。至于高阻态的大小姨妈,一般都是硬件内部(从机)的纠纷,鲜少与驱动方(主机)扯上关系,所以我们在此聊聊就好。

图23.8 写操作的物理时序(时序参数)。

图23.9 读操作的物理时序(时序参数)。

最后附上两张官方的原图23.8还有图23.9作为本说明的谢幕,读者就自己慢慢看着办吧。理解完毕,我们便可以开始建模了。

图23.10 DS1302基础模块的建模图。

图23.10是DS1302基础模块的建模图,内容包含控制模块与功能模块,功能模块负责最基础的读写操作,控制模块则负责功能调度,准备访问字节等任务。换之,功能模块的右边是驱动硬件资源的顶层信号。其中,功能模块的oData穿过控制模块直接驱动外围。

ds1302_funcmod.v

图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

图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

图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。对此,读者请自己探索然后尝试扩展该基础模块吧。

时间: 2024-10-02 23:55:27

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十三:DS1302模块的相关文章

【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读

前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例如UART,VGA等,但是<驱动篇I>贵就贵在建模技巧的升华,亦即低级建模II. 话说低级建模II,读过<建模篇>的朋友多少也会面熟几分,因为它是低级建模的进化形态.许久以前,笔者就有点燃低级建模II的念头,但是懒惰的性格让笔者别扭许久.某天,老大忽然说道:"让咱们大干一场吧

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验六:数码管模块

实验六:数码管模块 有关数码管的驱动,想必读者已经学烂了 ... 不过,作为学习的新仪式,再烂的东西也要温故知新,不然学习就会不健全.黑金开发板上的数码管资源,由始至终都没有改变过,笔者因此由身怀念.为了点亮多位数码管从而显示数字,一般都会采用动态扫描,然而有关动态扫描的信息请怒笔者不再重复.在此,同样也是动态扫描,但我们却用不同的思路去理解. 图6.1 6位数码管. 如图6.1所示,哪里有一排6位数码管,其中包好8位DIG信号还有6位SEL信号.DIG为digit,即俗称的数码管码,如果数码管

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击

实验三:按键模块② - 点击与长点击 实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作: l 电平变化检测: l 过滤抖动: l 产生有效按键. 实验三我们也会z执行同样的事情,不过却是产生不一样的有效按键: l 按下有效(点击): l 长按下有效(长点击). 图3.1 按下有效,时序示意图. 图3.2 长按下有效,时序示意图. 如图3.1所示,按下有效既是"点击",当按键被按下并且消抖完毕以后,isSClick信号就有被拉高一个时钟(Short Click).

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖

实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口水攻击 ... 面对它,笔者宛如被甩的男人,对它又爱又恨.不管怎么样,如今 I'll be back,笔者再也不会重复一样的悲剧. 按键消抖说傻不傻说难不难.所谓傻,它因为原理不仅简单(就是延迟几下下而已),而且顺序语言(C语言)也有无数不尽的例子.所谓难,那是因为人们很难从单片机的思维跳出来 ... 此外,

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击

实验四:按键模块③ — 单击与双击 实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块.在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键.实验四的按键功能模块有以下两项有效按键: l 单击(按下有效): l 双击(连续按下两下有效). 图4.1 单击有效按键,时序示意图. 实验四的“单击”基本上与实验三的“点击”一模一样,既按键被按下,经过消抖以后isSClick信号被拉高一个时钟,结果如图4.1所示,过程非常单调.反之,“双击”实现起来,会比较麻烦一些,因为我们还要

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验五:按键模块④ — 点击,长点击,双击

实验五:按键模块④ — 点击,长点击,双击 实验二至实验四,我们一共完成如下有效按键: l 点击(按下有效) l 点击(释放有效) l 长击(长按下有效) l 双击(连续按下有效) 然而,不管哪个实验都是只有两项“功能”的按键模块而已,如今我们要创建三项“功能”的按键模块,亦即点击(按下有效),长击,还有双击.实验继续之前,让我们先来复习一下各种有效按键. 图5.1 点击(按下有效). 如图5.1所示,所谓点击(按下有效)就是按键按下以后,isSClick信号(Single Click) 产生一

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验九:PS/2模块③ — 键盘与多组合键

实验九:PS/2模块③ — 键盘与多组合键 笔者曾经说过,通码除了单字节以外,也有双字节通码,而且双字节通码都是 8’hE0开头,别名又是 E0按键.常见的的E0按键有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC> 等编辑键.除此之外,一些组合键也是E0按键,例如 <RCtrl> 或者 <RAlt> .所以说,当我们设计组合键的时候,除了考虑“左边”的组合键以外,我们也要考虑“右边”的组合键.&

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验七:PS/2模块① — 键盘

实验七:PS/2模块① — 键盘 实验七依然也是熟烂的PS/2键盘.相较<建模篇>的PS/2键盘实验,实验七实除了实现基本的驱动以外,我们还要深入解PS/2时序,还有PS/2键盘的行为.不过,为了节省珍贵的页数,怒笔者不再重复有关PS/2的基础内容,那些不晓得的读者请复习<建模篇>或者自行谷歌一下. 市场上常见的键盘都是应用第二套扫描码,各种扫描码如图7.2所示.<建模篇>之际,笔者也只是擦边一下PS/2键盘,简单读取单字节通码与断码而已.所谓单字节通码,就是有效的按下

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验八:PS/2模块② — 键盘与组合键

实验八:PS/2模块② — 键盘与组合键 实验七之际,我们学习如何读取PS/2键盘发送过来的通码与断码,不过实验内容也是一键按下然后释放,简单按键行为而已.然而,实验八的实验内容却是学习组合键的按键行为. 不知读者是否有类似的经历?当我们使用键盘的时候,如果5~6按键同时按下,电脑随之便会发出“哔哔”的警报声,键盘立即失效.这是键盘限制设计,不同产品也有不同限制的按键数量.默认下,最大按键数量是5~7个.所谓组合键就是两个以上的按键所产生的有效按键.举例而言,按下按键 <A> 输出“字符a”,