Verilog HDL 之 卡式电话计费器
我们每天都在和手机打交道,更熟悉了打电话的各项业务,那么怎么通过Verilog HDL 硬件描述语言简单的控制电话的计时计费呢,下面我们就来看看是如何实现的。先介绍下卡式电话计费器的要求。
一、实验要求及原理
(1) 计费器在话卡插入后,能将卡中的币值读出并显示出来;在通话过程中,根据话务种类计话费并将话费从卡值中扣除,卡值余额每分钟更新一次;计时与计费数据均以十进制形式显示出来。
(2)话务分为3类:市话、长话和特话,其中市话按每分钟3角钱计费,长话按每分钟6角钱计费,特话不收费。当卡上余额不足时产生告警信号,当告警达到一定时间则切断当前通话。
二、实验平台
Quartus II 7.2 集成开发环境、SOPC-MBoard板、ByteBlaster II 下载电缆
三、实验实现
1、设计思路
此系统由三个模块组成,一是时钟分频模块,负责产生1Hz的时间;二是卡式电话计费主体,负责计时计费,余额不足时,产生警报后自动切断通话信号;三是顶层模块,负责数码管的显示。
2、在设计文件中输入Verilog代码。
(1)时钟分频
1 //--------------------------------------------------------------------------------------------------
2 // Title : clkgen
3 // Author : wangliang
4 // Generated : 2011.08.17
5 //-------------------------------------------------------------------------------------------------
6 `timescale 1 ns / 1 ps
7
8 module clkgen ( rst ,clkout ,clk );
9
10 input rst ;
11 wire rst ;
12 input clk ;
13 wire clk ;
14
15 output clkout ;
16 reg clkout ;
17
18 reg [31:0] count1;
19
20 always @ ( posedge clk or negedge rst)
21 begin
22 if ( rst== 1'b0 ) begin
23 clkout <= 1'b0;
24 count1 <= 17'b0;
25 end
26 else begin
27 if ( count1>=32'd25000000) begin
28 clkout <= ~clkout ;
29 count1 <= 32'd0;
30 end
31 else count1 <= count1 + 1'b1;
32 end
33 end
34
35 endmodule
产生1Hz的时间,供给主体部分的计时。
(2)卡式电话计费主体
1 //--------------------------------------------------------------------------------------------------
2 // Title : phone_account
3 // Author : wangliang
4 // Generated : 2011.08.17
5 //-------------------------------------------------------------------------------------------------
6 `timescale 1 ns / 1 ps
7
8 module phone_account ( num1,state ,warn ,dispmoney ,rst ,clk ,card ,write ,cut ,read ,decide ,disptime );
9
10 input state ;
11 wire state ;
12 input rst ;
13 wire rst ;
14 input card ;
15 wire card ;
16 input clk ;
17 wire clk ;
18 input [1:0] decide ;
19 wire [1:0] decide ;
20
21 output warn ;
22 reg warn ;
23 output [11:0] dispmoney ;
24 wire [11:0] dispmoney ;
25 output write ;
26 reg write ;
27 output cut ;
28 reg cut ;
29 output read ;
30 wire read ;
31 output [11:0] disptime ;
32 wire [11:0] disptime ;
33 output [7:0] num1 ;
34
35 reg [7:0] num1 ;
36 reg t1m ; //整分钟标志
37 reg set ;
38 reg [11:0] money ;
39 reg reset_ena ;
40 reg [11:0] dtime ;
41 wire clk_1Hz ;
42 reg [4:0] temp ;
43
44 clkgen clkgen (
45 .rst ( rst ),
46 .clk ( clk ),
47 .clkout ( clk_1Hz )
48
49 );
50
51 always @ ( posedge clk_1Hz or negedge rst )
52 begin
53 if ( rst == 1'b0 ) begin
54 num1 <= 1'b0 ;
55 t1m <= 1'b0 ;
56 end
57 else begin
58 if ( num1 >= 8'h59 ) begin
59 num1 <= 8'b0 ;
60 t1m <= 1'b1 ;
61 end
62 else begin
63 if ( num1[3:0] >= 4'b1001)
64 begin
65 num1[3:0] <= 4'b0 ;
66 num1[7:4] <= num1[7:4] + 1'b1 ;
67 end
68 else if ( card & state ) //当卡拿下来或者不打电话时,不计时
69 num1 <= num1 + 1'b1 ;
70 else
71 num1 <= 1'b0 ;
72 t1m <= 1'b0 ;
73 end
74 end
75 end
76
77 always @( posedge clk_1Hz or negedge rst )
78 begin
79 if ( rst == 1'b0 ) begin
80 money <= 12'b0 ;
81 dtime <= 12'b0 ;
82 set <= 1'b0 ;
83 end
84 else begin
85 if ( !set ) begin
86 money <= 12'h500 ; //初始值50元
87 set <= 1'b1;
88 end
89 if ( card & state )
90 if ( t1m )
91 case ( { state , decide } )
92 3'b101 : //市话
93 if ( money <= 11'd3 )begin
94 warn <= 1'b1 ;
95 write <= 1'b0 ;
96 reset_ena<= 1'b1 ;
97 end
98 else begin
99 if ( money [3:0] < 4'b0011) begin //计费
100 money [3:0] <= money[3:0] + 12'd7 ;
101 if ( money [7:4] != 0 )
102 money [7:4] <= money [7:4] -1'b1;
103 else begin
104 money [7:4] <= 12'd9 ;
105 money [10:8] <= money [10:8] - 1'b1;
106 end
107 end
108 else
109 money [3:0] <= money [3:0]- 3 ;
110 write <= 1'b1 ;
111
112 if ( dtime [3:0] >= 4'd9 ) begin //计时
113 dtime [3:0] <= 4'b0 ;
114 if ( dtime[7:4]>= 4'd9) begin
115 dtime [7:4] <= 4'b0 ;
116 dtime [11:8] <=dtime [11:8] + 1'b1 ;
117 end
118 else dtime [7:4] <= dtime [7:4] +1'b1 ;
119 end
120 else begin
121 dtime[3:0] <= dtime [3:0] + 1'b1;
122 warn <= 1'b0 ;
123 reset_ena <= 1'b0 ;
124 end
125 end
126 3'b110 : //长途
127 if ( money <= 12'd6 )begin
128 warn <= 1'b1 ;
129 write <= 1'b0 ;
130 reset_ena<= 1'b1 ;
131 end
132 else begin
133 if ( money [3:0] < 4'b0110) begin //计费
134 money [3:0] <= money[3:0] + 4 ;
135 if ( money [7:4] != 0 )
136 money [7:4] <= money [7:4] -1'b1;
137 else begin
138 money [7:4] <= 12'd9 ;
139 money [11:8] <= money [11:8] - 1'b1;
140 end
141 end
142 else
143 money [3:0] <= money [3:0]-6 ;
144 write <= 1'b1 ;
145
146 if ( dtime[3:0] >= 4'd9) begin //计时
147 dtime [3:0] <= 4'b0 ;
148 if ( dtime[7:4]>= 4'd9) begin
149 dtime [7:4] <= 4'b0 ;
150 dtime [11:8] <=dtime [11:8] + 1'b1 ;
151 end
152 else dtime [7:4] <= dtime [7:4] +1'b1 ;
153 end
154 else begin
155 dtime[3:0] <= dtime [3:0] + 1'b1;
156 reset_ena <= 1'b0 ;
157 warn <= 1'b0 ;
158 end
159 end
160 endcase
161 else write <= 1'b0;
162 else begin
163 dtime <= 1'b0 ;
164 warn <= 1'b0 ;
165 write <= 1'b0 ;
166 reset_ena <=1'b0 ;
167 end
168
169 end
170 end
171
172 always @ ( posedge clk_1Hz or negedge rst ) //当余额不足发出警报时,15S后自动切断通话信号
173
174 begin
175 if ( rst == 1'b0 ) begin
176 temp <= 5'b0 ;
177 end
178 else begin
179 if (warn) temp <= temp + 1'b1 ;
180 else temp <= 1'b0 ;
181 if ( temp >= 5'd15 )
182 begin cut <=1'b1 ;
183 temp <=1'b0 ;
184 end
185 if ( !card||!reset_ena ) begin
186 cut <= 1'b0 ;
187 temp <= 1'b0 ;
188 end
189 end
190 end
191
192 assign dispmoney = card? money :0 ;
193 assign disptime = dtime ;
194 assign read = card? 1: 0 ;
195
196 endmodule
从代码中可以看出,卡式电话计费主体是由4部分组成。
第44行~第49行:调用时钟分频模块,得到1Hz时钟;
第51行~第75行:秒针计时,当卡拿出来或者挂掉电话时,不计时,金钱初始值是50元,慢慢打吧。:-D
第77行~第170行:市话、长途的计分钟和计费,特话不收费;
第172行~第190行:当余额不足发出警报时,15S后自动切断通话信号。
(3)顶层显示
1 //--------------------------------------------------------------------------------------------------
2 // Title : top
3 // Author : wangliang
4 // Generated : 2011.08.17
5 //-------------------------------------------------------------------------------------------------
6 `timescale 1 ns / 1 ps
7
8 module top ( state ,warn ,rst ,clk ,card ,write ,cut ,read ,decide ,seven_seg );
9
10 input state ;
11 wire state ;
12 input rst ;
13 wire rst ;
14 input card ;
15 wire card ;
16 input clk ;
17 wire clk ;
18 input [1:0] decide ;
19 wire [1:0] decide ;
20
21 output warn ;
22 wire warn ;
23 output write ;
24 wire write ;
25 output cut ;
26 wire cut ;
27 output read ;
28 wire read ;
29 output [15:0] seven_seg ;
30 wire [15:0] seven_seg ;
31 wire [7:0] num1 ;
32
33 wire [11:0] disptime ;
34 wire [11:0] dispmoney ;
35
36 phone_account phone_account (
37 . state ( state ) ,
38 .warn( warn ) ,
39 .dispmoney( dispmoney ) ,
40 .rst( rst ) ,
41 .clk( clk ) ,
42 .card( card ) ,
43 .write( write ) ,
44 .cut( cut ) ,
45 .read( read ) ,
46 .decide( decide ) ,
47 .disptime( disptime ),
48 .num1(num1));
49
50 /*********************8数码管译码部分*************/
51
52 reg clkout ;
53 reg [31:0]cnt;
54 reg [2:0]scan_cnt ;
55 reg [3:0]A ;
56 parameter period= 100000;
57 reg [6:0] Y_r;
58 reg [7:0] DIG_r ;
59
60 assign seven_seg[7:0] = {1'b1,(~Y_r[6:0])};
61 assign seven_seg[15:8] =~DIG_r;
62
63
64 always @( posedge clk or negedge rst) //分频50Hz
65 begin
66 if (!rst) cnt <= 0 ;
67 else
68 begin
69 cnt<= cnt+1;
70 if (cnt == (period >> 1) - 1) //设定周期时间的一半
71 clkout <= #1 1'b1;
72 else if (cnt == period - 1) //设定的周期时间
73 begin
74 clkout <= #1 1'b0;
75 cnt <= #1 'b0;
76 end
77 end
78 end
79
80 always @(posedge clkout or negedge rst)
81 begin
82 if (!rst) scan_cnt <= 0 ;
83 else scan_cnt <= scan_cnt + 1;
84 end
85
86 always @( scan_cnt) //数码管选择
87 begin
88 case ( scan_cnt )
89 3'b000 :
90 begin
91 DIG_r <= 8'b0000_0001;
92 A <= disptime[3:0];
93 end
94 3'b001 :
95 begin
96 DIG_r <= 8'b0000_0010;
97 A <= disptime[7:4];
98 end
99 3'b010 :
100 begin
101 DIG_r <= 8'b0000_0100;
102 A <= disptime[11:8];
103 end
104 3'b011 :
105 begin
106 DIG_r <= 8'b0000_1000;
107 A <= dispmoney[3:0];
108 end
109 3'b100 :
110 begin
111 DIG_r <= 8'b0001_0000;
112 A <= dispmoney[7:4];
113 end
114 3'b101 :
115 begin
116 DIG_r <= 8'b0010_0000;
117 A <= dispmoney[11:8];
118 end
119 3'b110 :
120 begin
121 DIG_r <= 8'b0100_0000;
122 A <= num1[3:0];
123 end
124 3'b111 :
125 begin
126 DIG_r <= 8'b1000_0000;
127 A <= num1[7:4];
128 end
129 default :
130 begin
131 DIG_r <= 8'b0000_0000;
132 A <= 0;
133 end
134 endcase
135 end
136
137 always @ ( A ) //译码
138 begin
139 case (A )
140 0: Y_r = 7'b0111111; // 0
141 1: Y_r = 7'b0000110; // 1
142 2: Y_r = 7'b1011011; // 2
143 3: Y_r = 7'b1001111; // 3
144 4: Y_r = 7'b1100110; // 4
145 5: Y_r = 7'b1101101; // 5
146 6: Y_r = 7'b1111101; // 6
147 7: Y_r = 7'b0100111; // 7
148 8: Y_r = 7'b1111111; // 8
149 9: Y_r = 7'b1100111; // 9
150 10: Y_r = 7'b1110111; // A
151 11: Y_r = 7'b1111100; // b
152 12: Y_r = 7'b0111001; // c
153 13: Y_r = 7'b1011110; // d
154 14: Y_r = 7'b1111001; // E
155 15: Y_r = 7'b1110001; // F
156 default: Y_r = 7'b0000000;
157 endcase
158 end
159
160 endmodule
第86行~第135行:选择数码管的位置,共有8个数码管,最前面2位是显示秒针计时,中间3位是显示余额,后3位显示拨打了多少分钟;
第137行~第158行:数码管显示的数值。
3、由设计文件生成的.bsf文件,其外接接口如下图所示。
4、引脚分配
我们只需要对最后的一个图进行引脚分配,state接按键,表示接通还是挂断;rst接复位信号;clk接时钟信号;card接按键,表示卡是否插入;decide[1..0]接按键,表示拨打的是什么电话,市话?长途?特话?warn、write、cut、read都接LED灯,seven_seg接数码管。
5、实验结果
拨动按键就能看到LED灯和数码管的正确显示了。