设计背景:
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。
设计原理:
DS1302芯片的封装如下:
它有8个引脚,在我们的设计中我们只要驱动3个引脚就可以了,另外的引脚都是我们的硬件相连接的,和FPGA连接的有时钟先sclk,串行数据数据接口IO,以及CE.。
其工作原理就是在数据的传送过程中先把CE拉高,在每个数据的上升沿写入数据,在下降沿输入数据,一次只能读写一位数据。最初我们通过一个8位的控制指令来选择读写,如果控制指令是单字节模式,连续的8个脉冲上升沿写入,下降沿读出数据,一次只能读取一个字节,如果是突发模式通过连续的脉冲一次性的写完7个字节的时间寄存器也可以一次性的读完8--328位的ram数据。
控制指令有8位,第七位必须为高,如果是0写入被禁止,第六位0是对时钟寄存器的读写操作,为1对是控制对RAM区的读写操作。
bit1 -- 5 是对相关寄存器的操作,bit0是读写操作。
各个寄存器的的设置,和每位的表示如下图,图中高速我们读写时控制字是多少以及写入寄存器的地址,和写入的8位的时间表示我们可以通过下表来设置一个初始的时间。
涓流寄存器主要决定了DS1302的充电特性,涓流充电选择位4--7位,1010涓流充电其他禁止充电,二级管选择位3--2位,电阻选择1--0位具体的选择如下表:
突发模式下,表示我们可以连续写,连读,连续写时必须写满7个时钟寄存器。时序图如下,是SPI通信。
下表表示数据传送过程中数据的延迟,时钟的周期等时间。
本次设计是通过配置DS1302芯片来实现实时时钟的监测,我们通过通过控制2个按键来选择我们要在数码管上显示的时间,按下按键1我们来显示周几,按下按键2来显示年月日,不按显示时分秒,这样显示复合我们的数字表的显示。
我的思路是先打开写保护,一个一个寄存器的写进去,然后关闭写保护,然后再读出数据。
设计架构图:
设计代码:
顶层模块
0 module ds_1302(clk, rst_n, sclk, ce, data, sel ,seg7, key);
1
2 input clk, rst_n;
3 wire [23:0] shi_fen_miao;
4 wire [23:0] nian_yue_ri;
5 wire [23:0] day;
6 output sclk, ce;
7 inout data;
8 output [7:0] seg7;
9 output [5:0] sel;
10 input [1:0] key;
11
12 seg seg_dut(
13 .clk(clk),
14 .rst_n(rst_n),
15 .sel(sel),
16 .seg7(seg7),
17 .data_in(shi_fen_miao)
18 );
19
20 time_ce time_ce(
21 .clk(clk),
22 .rst_n(rst_n),
23 .data(data),
24 .sclk(sclk),
25 .show_data(shi_fen_miao),
26 .ce(ce),
27 .key(key)
28 );
29
30 endmodule
设计模块
0 module time_ce(clk, rst_n, data, sclk, show_data, ce, key);
1
2 input clk, rst_n;
3 inout data;
4 output reg sclk;
5 output reg ce;
6 input [1:0] key;
7
8 reg buff;
9 reg flag;
10 reg [7:0] times;
11 output reg [23:0] show_data;
12 reg [23:0] shi_fen_miao;
13 reg [23:0] nian_yue_ri;
14 reg [7:0] day;
15
16
17 assign data = flag ? buff : 1'bz; //定义一个三态,来控制IO口的输入输出
18
19 parameter w_1302 = 16'h8e00; //取消写保护
20 parameter w_miao = 16'h8030; // 30秒
21 parameter w_fen = 16'h8211; //11分
22 parameter w_shi = 16'h8408; //24小时制的 8 点
23 parameter w_ri = 16'h8621; //21日
24 parameter w_yue = 16'h8803; //3月
25 parameter w_nian = 16'h8c17; //2017年
26 parameter w_day = 16'h8a03; //星期3
27 parameter r_1302 = 16'h8e80; //写保护
28 parameter w_dian = 16'h90a7; //二个二极管8
29
30 //读寄存器的设置
31 parameter r_miao = 8'h81;
32 parameter r_fen = 8'h83;
33 parameter r_shi = 8'h85;
34 parameter r_ri = 8'h87;
35 parameter r_yue = 8'h89;
36 parameter r_nian = 8'h8d;
37 parameter r_day = 8'h8b;
38
39 reg [14:0] count;
40 reg [15:0] reg_data;
41 reg w_flag;
42 reg r_flag;
43 reg [1:0] state_r;
44
45 //定义一个序列及,来控制读写的时序
46 always @ (posedge clk)
47 if(!rst_n)
48 begin
49 count <= 0;
50 state_r <= 0;
51 end
52 else
53 case (state_r)
54 0 : if(r_flag || w_flag)
55 state_r <= 1;
56 else
57 state_r <= 0;
58 1 : if(count < (400 + 34 * 250))
59 count <= count + 1;
60 else
61 begin
62 state_r <= 0;
63 count <= 0;
64 end
65 default : state_r <= 0;
66 endcase
67
68 //开始ce为低,然后拉高,过上最少4US后拉高SCLK
69 //在写的时候我们控制总线,写入命令,读的时候释放总线
70 //在上升沿写入数据,我们就要在上升沿前准备数据,
71 //在下降沿读出数据,我们就要在下降沿后接收数据
72 reg [7:0] time_temp;
73 always @ (posedge clk)
74 if(!rst_n)
75 begin
76 sclk <= 0;
77 ce <= 0;
78 flag <= 0;
79 times <= 0;
80 buff <= 0;
81 time_temp <= 0;
82 end
83 else
84 case (count)
85 20 : begin ce <=1; flag <= 1; end
86 400 + 1 * 250 : begin sclk <= 0; buff <= reg_data[8]; end
87 400 + 2 * 250 : begin sclk <= 1; end
88 400 + 3 * 250 : begin sclk <= 0; buff <= reg_data[9];end
89 400 + 4 * 250 : begin sclk <= 1; end
90 400 + 5 * 250 : begin sclk <= 0; buff <= reg_data[10]; end
91 400 + 6 * 250 : begin sclk <= 1; end
92 400 + 7 * 250 : begin sclk <= 0; buff <= reg_data[11]; end
93 400 + 8 * 250 : begin sclk <= 1; end
94 400 + 9 * 250 : begin sclk <= 0; buff <= reg_data[12];end
95 400 + 10 * 250 : begin sclk <= 1; end
96 400 + 11 * 250 : begin sclk <= 0; buff <= reg_data[13];end
97 400 + 12 * 250 : begin sclk <= 1; end
98 400 + 13 * 250 : begin sclk <= 0; buff <= reg_data[14]; end
99 400 + 14 * 250 : begin sclk <= 1; end
100 400 + 15 * 250 : begin sclk <= 0; buff <= reg_data[15]; end
101
102 400 + 16 * 250 : if(w_flag)
103 sclk <= 1;
104 else if(r_flag)
105 begin sclk <= 1; end
106
107 400 + 17 * 250 : if(w_flag)
108 begin sclk <= 0; buff <= reg_data[0]; end
109 else if(r_flag)
110 begin sclk <= 0;flag <= 0;end
111
112 400 + 18 * 250 : if(w_flag)
113 sclk <= 1;
114 else if(r_flag)
115 begin sclk <= 1; times[0] <= data; end
116
117 400 + 19 * 250 : if(w_flag)
118 begin sclk <= 0; buff <= reg_data[1]; end
119 else if(r_flag)
120 sclk <= 0;
121
122 400 + 20 * 250 : if(w_flag)
123 sclk <= 1;
124 else if(r_flag)
125 begin sclk <= 1; times[1] <= data; end
126
127
128 400 + 21 * 250 : if(w_flag)
129 begin sclk <= 0; buff <= reg_data[2]; end
130 else if(r_flag)
131 sclk <= 0;
132
133 400 + 22 * 250 : if(w_flag)
134 sclk <= 1;
135 else if(r_flag)
136 begin sclk <= 1; times[2] <= data; end
137
138 400 + 23 * 250 : if(w_flag)
139 begin sclk <= 0; buff <= reg_data[3]; end
140 else if(r_flag)
141 sclk <= 0;
142
143 400 + 24 * 250 : if(w_flag)
144 sclk <= 1;
145 else if(r_flag)
146 begin sclk <= 1; times[3] <= data; end
147
148 400 + 25 * 250 : if(w_flag)
149 begin sclk <= 0; buff <= reg_data[4]; end
150 else if(r_flag)
151 sclk <= 0;
152
153 400 + 26 * 250 : if(w_flag)
154 sclk <= 1;
155 else if(r_flag)
156 begin sclk <= 1; times[4] <= data; end
157
158 400 + 27 * 250 : if(w_flag)
159 begin sclk <= 0; buff <= reg_data[5]; end
160 else if(r_flag)
161 sclk <= 0;
162
163 400 + 28 * 250 : if(w_flag)
164 sclk <= 1;
165 else if(r_flag)
166 begin sclk <= 1; times[5] <= data; end
167
168 400 + 29 * 250 : if(w_flag)
169 begin sclk <= 0; buff <= reg_data[6]; end
170 else if(r_flag)
171 sclk <= 0;
172
173 400 + 30 * 250 : if(w_flag)
174 sclk <= 1;
175 else if(r_flag)
176 begin sclk <= 1; times[6] <= data; end
177
178 400 + 31 * 250 : if(w_flag)
179 begin sclk <= 0; buff <= reg_data[7]; end
180 else if(r_flag)
181 sclk <= 0;
182
183 400 + 32 * 250 : begin sclk <= 1; times[7] <= data; end
184 400 + 33 * 250 : begin sclk <= 0; if(w_flag) flag <= 0; else if(r_flag) flag <= 1;end
185 400 + 34 * 250 : ce <= 0;
186 400 + 35 * 250 : ce <= 1;
187
188 endcase
189
190 //一下模块是控制模块,每当写完一个寄存器后,计数会到400+35*25
191 //然后跳转下一个状态执行下一个寄存器命令
192 //在读模式下,计数到400+35*25是表示读出了一个时间我们接收
193 //放到寄存器中
194
195 reg [5:0] state;
196 always @ (posedge clk)
197 if(!rst_n)
198 begin
199 reg_data <= 0;
200 w_flag <= 0;
201 r_flag <= 0;
202 state <= 0;
203 end
204 else
205 case (state)
206 0 : begin
207 w_flag <= 1;
208 reg_data <= w_1302;
209 state <= 1;
210 end
211
212 1 : begin
213 if(count == 400 + 34 * 250)
214 begin
215 reg_data <= w_miao;
216 state <= 2;
217 end
218 else
219 state <= 1;
220 end
221
222 2 : begin
223 if(count == 400 + 34 * 250)
224 begin
225 reg_data <= w_fen;
226 state <= 3;
227 end
228 else
229 state <= 2;
230 end
231 3 : begin
232 if(count == 400 + 34 * 250)
233 begin
234 reg_data <= w_shi;
235 state <= 4;
236 end
237 else
238 state <= 3;
239 end
240
241 4 : begin
242 if(count == 400 + 34 * 250)
243 begin
244 reg_data <= w_ri;
245 state <= 5;
246 end
247 else
248 state <= 4;
249 end
250
251 5 : begin
252 if(count == 400 + 34 * 250)
253 begin
254 reg_data <= w_yue;
255 state <= 6;
256 end
257 else
258 state <= 5;
259 end
260
261 6 : begin
262 if(count == 400 + 34 * 250)
263 begin
264 reg_data <= w_nian;
265 state <= 7;
266 end
267 else
268 state <= 6;
269 end
270
271 7 : begin
272 if(count == 400 + 34 * 250)
273 begin
274 reg_data <= w_day;
275 state <= 8;
276 end
277 else
278 state <= 7;
279 end
280
281 8 : begin
282 if(count == 400 + 34 * 250)
283 begin
284 reg_data <= r_1302;
285 state <= 9;
286 end
287 else
288 state <= 8;
289 end
290
291
292 ///////////////////////
293
294 9 : begin
295 if(count == (400 + 250 * 34 - 1))
296 begin
297 w_flag <= 0;
298 r_flag = 1;
299 state <= 10;
300 end
301 else
302 state <= 9;
303 end
304 /////////////////时分秒
305 10 : begin
306 state <= 11;
307 end
308
309 11 : begin
310 if(count == 400 + 34 * 250)
311 begin
312 state <= 12;
313 shi_fen_miao[7:0] <= times;
314 end
315 else
316 begin
317 state <= 11;
318 reg_data[15:8] <= r_miao;
319 end
320 end
321
322 12 : begin
323 if(count == 400 + 34 * 250)
324 begin
325 state <= 13;
326 shi_fen_miao[15:8] <= times;
327 end
328 else
329 begin
330 state <= 12;
331 reg_data[15:8] <= r_fen;
332 end
333 end
334
335 13 : begin
336 if(count == 400 + 34 * 250)
337 begin
338 shi_fen_miao[23:16] <= times;
339 state <= 14;
340 end
341 else
342 begin
343 reg_data[15:8] <= r_shi;
344 state <= 13;
345 end
346 end
347
348 //////////////// 年月日
349 14 : begin
350 if(count == 400 + 34 * 250)
351 begin
352 state <= 15;
353 nian_yue_ri[7:0] <= times;
354 end
355 else
356 begin
357 state <= 14;
358 reg_data[15:8] <= r_ri;
359 end
360 end
361
362 15 : begin
363 if(count == 400 + 34 * 250)
364 begin
365 state <= 16;
366 nian_yue_ri[15:8] <= times;
367 end
368 else
369 begin
370 state <= 15;
371 reg_data[15:8] <= r_yue;
372 end
373 end
374
375 16 : begin
376 if(count == 400 + 34 * 250)
377 begin
378 nian_yue_ri[23:16] <= times;
379 state <= 17;
380 end
381 else
382 begin
383 reg_data[15:8] <= r_nian;
384 state <= 16;
385 end
386 end
387
388 ////////周
389 17 : begin
390 if(count == 400 + 34 * 250)
391 begin
392 day <= times;
393 state <= 10;
394 end
395 else
396 begin
397 reg_data[15:8] <= r_day;
398 state <= 17;
399 end
400 end
401
402 default: state <= 0;
403 endcase
404
405 //按键判断需要输出的时间是什么
406 always @ (posedge clk)
407 if(!rst_n)
408 begin
409 show_data <= 0;
410 end
411 else if (!key[0])
412 show_data <= day;
413 else if (!key[1])
414 show_data <= nian_yue_ri;
415 else
416 show_data <= shi_fen_miao;
417
418 endmodule
数码管模块
0 module seg(clk,rst_n,sel,seg7,data_in);
1
2 input clk;
3 input rst_n;
4 input [23:0] data_in;
5 output reg [5:0] sel;
6 output reg [7:0] seg7;
7
8 parameter s0 = 3'b000;
9 parameter s1 = 3'b001;
10 parameter s2 = 3'b010;
11 parameter s3 = 3'b011;
12 parameter s4 = 3'b100;
13 parameter s5 = 3'b101;
14
15 `define T1ms 50_000
16 //`define T1ms 5
17 reg [15:0] count;
18 reg flag;
19
20 //分频模块 1K的时钟
21 always @ (posedge clk or negedge rst_n)
22 if(!rst_n)
23 begin
24 count <= 16'd0;
25 flag <= 1;
26 end
27 else
28 if(count == (`T1ms / 2 - 1))
29 begin
30 count <= 16'd0;
31 flag <= ~ flag;
32 end
33 else
34 begin
35 count <= count + 1'b1;
36 end
37
38 reg [2:0] state;
39 reg [3:0] num;
40
41 //循坏扫描,选择那一个数码管显示哪一位
42 always @ (posedge flag or negedge rst_n)
43 if(!rst_n)
44 begin
45 sel <= 3'b0;
46 state <= 3'b0;
47 num <= 4'b0;
48 end
49 else
50 begin
51 case (state)
52 s0:begin
53 state <= s1;
54 sel <= 6'b011111;
55 num <= data_in[23:20];
56 end
57 s1:begin
58 state <= s2;
59 sel <= 6'b101111;
60 num <= data_in[19:16];
61 end
62 s2:begin
63 state <= s3;
64 sel <= 6'b110111;
65 num <= data_in[15:12];
66 end
67 s3:begin
68
69 state <= s4;
70 sel <= 6'b111011;
71 num <= data_in[11:8];
72
73 end
74 s4:begin
75 state <= s5;
76 sel <= 6'b111101;
77 num <= data_in[7:4];
78 end
79 s5:begin
80 state <= s0;
81 sel <= 6'b111110;
82 num <= data_in[3:0];
83 end
84 default:state <= s0;
85 endcase
86 end
87
88 //编码
89 always @ (*)
90 begin
91 case (num)
92 0:seg7 = 8'b1100_0000;
93 1:seg7 = 8'b1111_1001;
94 2:seg7 = 8'b1010_0100;
95 3:seg7 = 8'b1011_0000;
96 4:seg7 = 8'b1001_1001;
97 5:seg7 = 8'b1001_0010;
98 6:seg7 = 8'b1000_0010;
99 7:seg7 = 8'b1111_1000;
100 8:seg7 = 8'b1000_0000;
101 9:seg7 = 8'b1001_0000;
102 10:seg7 = 8'b1000_1000;
103 11:seg7 = 8'b1000_0011;
104 12:seg7 = 8'b1100_0110;
105 13:seg7 = 8'b1010_0001;
106 14:seg7 = 8'b1000_0110;
107 15:seg7 = 8'b1000_1110;
108 default:;
109 endcase
110 end
111 endmodule
测试模块
0 `timescale 1ns/ 1ps
1
2 module tb;
3
4 reg clk, rst_n;
5
6 wire sclk, ce;
7 wire data;
8 wire [7:0] seg7;
9 wire [5:0] sel;
10
11 initial begin
12 clk = 1;
13 rst_n = 0;
14
15 #200.1 rst_n = 1;
16
17 end
18
19 always #10 clk = ~clk;
20
21
22 ds_1302 dut(
23 .clk(clk),
24 .rst_n(rst_n),
25 .sclk(sclk),
26 .ce(ce),
27 .data(data),
28 .sel (sel),
29 .seg7(seg7)
30 );
31
32 endmodule
仿真图:
我们的测试图中我们可以清楚的看到我们在发送的寄存器命令,时收回总线控制权,读数据时释放了总线控制权。
下图中是突发读的时序图,我的设计是我们一个一个的写寄存器,必须写满7个时钟寄存器,然后突发的读,突发读的时候拉高ce然后不停的读接收数据就行了。
有兴趣的的朋友可以在我设计的基础上,做出突发读写,这样也是对大家的提高。
全部0条评论
快来发表一下你的评论吧 !