這周有朋友問怎樣在fpga中用數碼管來顯示一個十進制數,比如1000。每個數碼管上顯示一位十進制數。如果用高級語言來分離各位,只需要分別對該數做1000,100,10對應的取商和取余即可分離出千百十個位。但是FPGA做除法非常耗資源。有沒有其它解決辦法?因為用verilog寫程序時雖然形式上可以寫為比如256,但是實際存儲對應的還是0100H,且一個數碼管只能顯示一個十進制數。因此這個問題相當於二進制如何轉換為一個BCD(Binary Code Decimal)碼數。
本文只考慮最常見的8421碼的轉換,而且是壓縮BCD碼。
一、算法原理
根據二進制與十進制轉換的定義可知,將二進制數按位加權求和即可得到。用公式描述為n位二進制轉換10進制公式
A(n-1)*2^(n-1)+A(n-2)*2^(n-2)+........+A(0)*2^0=對應十進制數 (1)
將公式(1)一直提取公因子2,可很容易將公式(1)轉換為公式(2),如下所示:
(A(n-1)*2+A(n-2))*2+A(n-3))*2........+a(0) =對應十進制數 (2)
即對於n位二進制數的按權展開求和,由最高的2的n-1次冪,轉換為乘以n-1次2,以此類推n-2次冪,n-3次冪……。因為左移一位相當於乘以2,這樣乘法操作就轉換為了左移操作。
1、BCD碼有0~9共計10個數碼,用四位二進制表示0000~1001即可表示,而四位二進制可以表示數的范圍為0000~1001~1111,即0~9~15。總計多處了 6個數碼A~F。我們知道十進制逢十進一,十六進制逢十六進一。從而產生了一個非常關鍵的問題,轉換到大於等於10以后的數字后本應由低位向緊鄰的高位進位,但是只能到大於16以后進位。如DH(12D)十進制本應進位產生十位1,和個位2,但是卻變成十六進制的DH。所以必須進行修正,修正的方法是加6。
2、BCD碼是二進制編碼的十進制,逢十進一,10/2=5.因此得到判斷條件,即判斷每四位是否大於4,因為5-9進一位(左移)溢出。
3、對於左移操作,相當於每進一位就會丟掉6,那么就要加上6/2=3(3左移一位后相當於6)。每次調整在左移之前完成。
*因此二進制轉BCD的方法是通過左移,然后每四位判斷是否大於4,滿足則加3.
二、基於Verilog的代碼實現
為了簡便,本代碼是以十進制數1000為例進行測試。1000D = 3E8H = 0011 1110 1000B。
1 /******************************************************************* 2 * 功能:將二進制數轉換為BCD碼數,在七段數碼管上顯示 3 * 原理:左移 + 3 4 * 作者:國靜德遠 5 * 時間:2017年4月16日 6 *******************************************************************/ 7 module BinaryToBCD(CLOCK_50, HEX3, HEX2, HEX1, HEX0); 8 9 input CLOCK_50; 10 output [7:0] HEX3, HEX2, HEX1, HEX0; 11 12 wire [9:0] TestData; 13 wire [15:0] ResultData; 14 assign TestData = 10'd1001; 15 //Binary to BCD 16 Binary2BCD ConvertData(CLOCK_50, TestData, ResultData); 17 //Display 18 Seg7 H1(ResultData[ 3 : 0], HEX0); 19 Seg7 H2(ResultData[ 7 : 4], HEX1); 20 Seg7 H3(ResultData[11 : 8], HEX2); 21 Seg7 H4(ResultData[15 : 12], HEX3); 22 23 endmodule 24 25 //Convert Data by left + 3 26 module Binary2BCD(inClk, inData, outData); 27 input inClk; 28 input [9:0] inData; 29 output [15:0]outData; 30 31 32 reg [3:0]count10 = 9; 33 reg [15:0]ShiftReg; 34 35 36 always@(posedge inClk) 37 begin 38 if(count10 >= 0 && count10 <= 9) 39 begin 40 count10 <= count10 - 1'b1; 41 end 42 else 43 count10 <= 4'd15; 44 end 45 46 always @(posedge inClk) 47 begin 48 //for(i = 9; i >= 0; i = i - 1) 49 if(count10 >= 0 && count10 <= 9) 50 begin 51 // shift left 52 ShiftReg = (ShiftReg << 1); 53 ShiftReg[0] = inData[count10]; 54 //adjust by add 3 55 if(ShiftReg[15:12] > 4) 56 ShiftReg[15:12] = ShiftReg[15:12] + 2'd3; 57 else 58 ShiftReg[15:12] = ShiftReg[15:12]; 59 60 if(ShiftReg[11:8] > 4) 61 ShiftReg[11:8] = ShiftReg[11:8] + 2'd3; 62 else 63 ShiftReg[11:8] = ShiftReg[11:8]; 64 65 if(ShiftReg[7:4] > 4) 66 ShiftReg[7:4] = ShiftReg[7:4] + 2'd3; 67 else 68 ShiftReg[7:4] = ShiftReg[7:4]; 69 70 if(ShiftReg[3:0] > 4) 71 ShiftReg[3:0] = ShiftReg[3:0] + 2'd3; 72 else 73 ShiftReg[3:0] = ShiftReg[3:0]; 74 75 76 end 77 else 78 ShiftReg = ShiftReg; 79 end 80 81 assign outData = ShiftReg; 82 83 endmodule 84 85 //Display on 7seg 86 module Seg7(inData, outData); 87 input [3:0]inData; 88 output [7:0]outData; 89 //reg [3:0]inData; 90 reg [7:0]outData; 91 always@(inData) 92 begin 93 case(inData) 94 4'd0: outData = 8'b1100_0000; 95 4'd1: outData = 8'b1111_1001; 96 4'd2: outData = 8'b1010_0100; 97 4'd3: outData = 8'b1011_0000; 98 4'd4: outData = 8'b1001_1001; 99 4'd5: outData = 8'b1001_0010; 100 4'd6: outData = 8'b1000_0010; 101 4'd7: outData = 8'b1111_1000; 102 4'd8: outData = 8'b1000_0000; 103 4'd9: outData = 8'b1001_0000; 104 default: outData = 8'b1111_1111; 105 endcase 106 end 107 endmodule
仿真結果如下圖所示:

