数码管动态显示
二进制码转变为BCD码
- 动态显示驱动模块,是将传入的待显示的十进制数据,转换为位选和段选信号,传入的数据data是由数据产生模块产生的
- 二进制表示的十进制数需要转变为BCD码表示的十进制数,从而产生位选和段选信号
- BCD码是使用四位二进制数表示二进制数
- BCD码分为有权码(8421码\5421码\2421码)和无权码(余三码\余三循环码\格雷码)
- 8421码是最常使用的BCD码
BCD码产生段选信号和位选信号的优势?
- 十进制数234,使用二进制数表示1110_1010,使用8421码进行表示0010_0011_0100),在第一个显示周期内显示个位,在第二个显示周期内显示第二个十位数码管...扫描频率足够快的时候,可以认为是同时显示,所以要分别得到个\十\百\千\万....值,使用BCD码进行表示可以直观的得到其中的个\十\百\千\万位
二进制码转换为BCD码
- 首先进行补0操作,六位数码管显示的最大值是999_999,转变为6 bit BCD码,所以补24个0
- 判断运算和移位操作,首先判断每一位BCD码,如果每一位BCD码<=4,保持数值不变,如果每一位BCD码>4,那么就将BCD码+3
- 判断运算结束之后,不管是否进行加三操作,都需要将数据左移一位
- 当移位计算到原数据的二进制数的值为0的时候,可以得到相应的BCD码
- 移位次数=原来数据的二进制码的位宽
系统框图
波形图
- 二进制向BCD码转换是通过判断运算和移位操作实现的
- 移位操作的次数和数据的二进制长度有关,需要一个移位计数器cnt_shift [4:0],实际上移位20次即可,计数器的最大值是19,但是增加两个状态,一个是初始补零的状态,一个是最后提取数位的状态
- 在进行移位操作的时候需要中间变量记录中间数据data_shift[43:0],并且要从中间数据中提取出相应的数据位(个/时/百/...)的数据
- 判断运算在移位之前进行,并且每次cnt_shift值的更新都是在判断运算和移位操作之后进行的,所以需要一个标志信号(shift_flag),表示移位操作,同时可以作为cnt_shift值更新的信号,
- 判断运算和移位操作是在一个周期内完成的
- shift_flag信号,复位信号有效的时候,初始值位0,时钟信号无效的时候,在时钟上升沿不断对其进行取反,相当于对时钟进行二分频
- shift_flag为高电平的时候,进行移位操作,cnt_shift进行加1计数,shift_flag为低电平的时候进行判断运算
- cnt_shift计数到最大值并且shift_flag信号为高电平的时候,进行清0
- 输出信号是个/十/百/千...位
RTL
```verilog
module bcd_8421
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire [19:0] data , //输入需要转换的数据
output reg [3:0] unit , //个位BCD码
output reg [3:0] ten , //十位BCD码
output reg [3:0] hun , //百位BCD码
output reg [3:0] tho , //千位BCD码
output reg [3:0] t_tho , //万位BCD码
output reg [3:0] h_hun //十万位BCD码
);
//********************************************************************//
//******************** Parameter And Internal Signal *****************//
//********************************************************************//
//reg define
reg [4:0] cnt_shift ; //移位判断计数器
reg [43:0] data_shift ; //移位判断数据寄存器
reg shift_flag ; //移位判断标志信号
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_shift:从0到21循环计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_shift <= 5'd0;
else if((cnt_shift == 5'd21) && (shift_flag == 1'b1))
cnt_shift <= 5'd0;
else if(shift_flag == 1'b1)
cnt_shift <= cnt_shift + 1'b1;
else
cnt_shift <= cnt_shift;
//data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_shift <= 44'b0;
else if(cnt_shift == 5'd0)
data_shift <= {24'b0,data};
else if((cnt_shift <= 20) && (shift_flag == 1'b0))
begin
data_shift[23:20] <= (data_shift[23:20] > 4) ? (data_shift[23:20] + 2'd3) : (data_shift[23:20]);
data_shift[27:24] <= (data_shift[27:24] > 4) ? (data_shift[27:24] + 2'd3) : (data_shift[27:24]);
data_shift[31:28] <= (data_shift[31:28] > 4) ? (data_shift[31:28] + 2'd3) : (data_shift[31:28]);
data_shift[35:32] <= (data_shift[35:32] > 4) ? (data_shift[35:32] + 2'd3) : (data_shift[35:32]);
data_shift[39:36] <= (data_shift[39:36] > 4) ? (data_shift[39:36] + 2'd3) : (data_shift[39:36]);
data_shift[43:40] <= (data_shift[43:40] > 4) ? (data_shift[43:40] + 2'd3) : (data_shift[43:40]);
end
else if((cnt_shift <= 20) && (shift_flag == 1'b1))
data_shift <= data_shift << 1;
else
data_shift <= data_shift;
//shift_flag:移位判断标志信号,用于控制移位判断的先后顺序
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
shift_flag <= 1'b0;
else
shift_flag <= ~shift_flag;
//当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
unit <= 4'b0;
ten <= 4'b0;
hun <= 4'b0;
tho <= 4'b0;
t_tho <= 4'b0;
h_hun <= 4'b0;
end
else if(cnt_shift == 5'd21)
begin
unit <= data_shift[23:20];
ten <= data_shift[27:24];
hun <= data_shift[31:28];
tho <= data_shift[35:32];
t_tho <= data_shift[39:36];
h_hun <= data_shift[43:40];
end
endmodule