上篇写了倒计时系统的计时模块,这篇接着写倒计时系统的BCD编码模块。首先介绍下,BCD码的定义。 BCD码是指用4位二进制数来表示1位十进制数0——9中的一个。
设计此模块的原因:假如上一篇计时模块的输出cnt_s=21、cnt_ms=45(即21.45),它是二进制数;而数码管显示是将cnt_s=21/cnt_ms=45的个位和十位分别显示的。故此需要将二进制数转化成BCD码。
(一)二进制数转BCD码的原理(加3移位算法)
首先,定义一个4*N位的BCD码,如bcd_data[7:0]=8'h00,需被转换的二进制数定义成din[7:0]。
然后,将din[7]移入bcd_data[0],将bcd_data[7:0]每四位分成一组,这4位组成的二进制数若>=5,则加3后再赋值于bcd_data[7:0];若<5,则将bcd_data[7:0]保持不变。依次进行上步操作,直至将din[7:0]的数值全部移入bcd_data[7:0]中。
需要注意的是,被转换的二进制数din是N位,则需要转移的次数就是N次,判断的次数是(N-1)次。
(二) BCD编码的模块框图:
(三)源代码:
module bcd(
//System Signals
input sclk ,
input rst_n ,
//Others
input bcd_trig ,
input [7:0] din , //max is 99
//cnt_ms[7:0],cnt_s[7:0]
output reg [7:0] bcd_data , //two smg:4bit+4bit
//bcd_cnt_ms[7:0],bcd_cnt_s[7:0]
output reg bcd_data_vld
);
//========================================================================\
// **********Define Parameter and Internal Signals*******************
//========================================================================/
parameter END_SHIFT = 8 ; //din is 8 bit
parameter IDLE = 3'b001 ;
parameter SHIFT = 3'b010 ;
parameter ADD = 3'b100 ;
reg [7:0] din_temp ;
reg [3:0] bcd_data_r1 ; //unit
reg [3:0] bcd_data_r2 ; //ten
reg [2:0] state ;
reg [3:0] cnt_shift ;
reg flag_bcd ;
//========================================================================\
// **********Main Code*******************
//========================================================================/
//flag_bcd
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
flag_bcd<=1'b0;
else if(bcd_trig==1'b1)
flag_bcd<=1'b1;
else if(bcd_data_vld==1'b1)
flag_bcd<=1'b0;
end
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
begin
bcd_data_r1<=4'b0000;
bcd_data_r2<=4'b0000;
bcd_data<=8'd0;
bcd_data_vld<=1'b0;
cnt_shift<=4'd0;
din_temp<=8'd0;
state<=IDLE;
end
else //if(flag_bcd)
begin
case(state)
IDLE: begin
bcd_data_vld<=1'b0;
if(flag_bcd==1'b1)
begin
din_temp<=din;
bcd_data_r1<=4'b0000;
bcd_data_r2<=4'b0000;
state<=SHIFT;
end
else state=IDLE;
end
SHIFT: begin
if(cnt_shift<END_SHIFT)
begin
bcd_data_r1<={bcd_data_r1[2:0],din_temp[7]};
bcd_data_r2<={bcd_data_r2[2:0],bcd_data_r1[3]};
din_temp<={din_temp[6:0],1'b0};
cnt_shift<=cnt_shift+1'b1;
state<=ADD;
end
else begin
bcd_data_vld<=1'b0;
state<=IDLE;
end
end
ADD: begin
if(cnt_shift<END_SHIFT)
begin
if(bcd_data_r1>='d5)
bcd_data_r1<=bcd_data_r1+'d3;
else bcd_data_r1<=bcd_data_r1;
if(bcd_data_r2>='d5)
bcd_data_r2<=bcd_data_r2+'d3;
else bcd_data_r2<=bcd_data_r2;
state<=SHIFT;
end
else begin
bcd_data<={bcd_data_r2,bcd_data_r1};
bcd_data_vld<=1'b1;
cnt_shift<='d0;
state<=IDLE;
end
end
default: begin
state<=IDLE;
cnt_shift<='d0;
end
endcase
end
end
endmodule
(四)这个模块主要使用了一个状态机。这个状态机也比较简单,一共三个状态,分别是初始态(IDLE)、移位态(SHIFT)和判断加3态(ADD)。下面是其状态转移图:
(五)总结
在这个模块中也使用了自己代码的一贯风格:
A. 定义了此模块被触发的开始信号bcd_trig;
B. BCD编码正在工作状态的信号flag_bcd;
C. BCD编码完成信号bcd_data_vld.
|