作科研的時候從學校拿到一塊基於Xilinx公司Spartan-6主芯片的FPGA開發板,由於以前一直在用Altera公司的FPGA,一開始接觸它還真有點不太習慣。但畢竟核心的東西仍是不會變的,因而按照慣例,先仔細瞄了瞄這塊開發板,看看有哪些可用的資源--撥碼開關、按鍵、LED、七段數碼管、USB Host、USB UART、VGA、以太網接口,嗯哼,雖然比不上友晶的DE2那麼強大,可是看來作通常的開發仍是綽綽有餘的。ide
瞄完就是上網找資料了,首先找是板子的製造商--digilent,下載原理圖,下載能用的開發工具,下載DEMO,而後就是學着怎麼操做Xilinx的開發工具,將DEMO下載上去,肯定板子有沒有問題(這其間從熟悉的quartus轉到陌生的Xilinx ISE着實花了我一番功夫,這裏就不羅嗦了)。digilent上有一個DEMO挺有意思的,叫Nexys3_ISE_GPIO_UART的工程,它將撥碼開關、按鍵、LED、七段數碼管、USB UART這幾種板子上的基礎資源寫進了一個工程裏,效果好象是撥碼開關控制LED,數碼管循環顯示幾個單詞,而後用USB數據線將UART口跟電腦相連,能經過按鍵控制向電腦發送特定的字符串,而後能在電腦上的超級終端接收到這個字符,看着挺牛的嘛!不過那老是人家的,要變成本身的還得本身動手!說幹就幹,那就拿他練手吧。工具
按理來講這也不難吧,畢竟有現成的東西在,我開始也這麼想的,不過當我打開那個DEMO裏面的代碼一看,頓時有種想罵人的衝動--竟然是VHDL!我一開始學的就是Verilog,VHDL壓根就沒接觸過,這兩種語言差異還有點大呢!看這裏面的代碼,就像天書!要我看懂這些傢伙我還不如本身研究呢(其實也沒那麼恐怖)!因而就準備本身寫一份測試的DEMO,就當是練手了。性能
本來的DEMO功能有點多,因而我就想把它分兩部分來完成,把比較複雜的USB UART這部分單獨拿出來,其餘的放在一個DEMO裏面。後來又想,既然有4個數碼管,那就作個時鐘吧,雖然沒有小時部分,但做爲練手仍是無傷大雅的。這篇文章先說說這個數碼管時鐘,USB UART部分將在後面的文章中討論。開發工具
數碼管時鐘我想對於不少學電子的同窗來講都不陌生了,特別是學過單片機的,這個東西是入門練手必備的。閒話很少說,先來講說這個原理。怎麼點亮單個數碼管我就不說了,無非分清共陰共陽,而後就是置高置低的問題。可是對於4個一塊兒的數碼管,要怎麼控制他們看起來好像是在同時點亮的並且還能顯示不一樣的內容就比較麻煩了。先上原理圖:測試
顯然要控制一個數碼管須要兩部分的端口--CA...DP和AN0...AN3,我通常叫左邊的爲段選,右邊的爲位選。從原理圖上來看,位選端接在PNP管的B極,要先讓C、E極導通,相應的位選端應該是低電平,好比AN3...AN0四位爲1110,就是選擇了最後一個數碼管。而當數碼管選中時(即相應的三極管導通),數碼管公共端接的是高電平,顯然這些數碼管應該是共陽的,因而CA...DP端的編碼就能夠按共陽數碼管的編碼方式作了。轉換成Verilog代碼,能夠是一下的常數定義:編碼
parameter AN1 = 4'b1110,AN2 = 4'b1101,AN3 = 4'b1011,AN4 = 4'b0111;//數碼管位選定義 parameter zero = 8'b0000_0011,one = 8'b1001_1111,two = 8'b0010_0101,three = 8'b0000_1101,four = 8'b1001_1001, five = 8'b0100_1001,six = 8'b0100_0001,seven = 8'b0001_1111,eigth = 8'b0000_0001,nine = 8'b0000_1001;//數碼管段選定義
好了,如今搞清楚了第一個問題--怎麼將0-9幾個數顯示出來,接下來的問題是怎麼讓4個數碼管看起來是一塊兒顯示的,並且還能顯示不一樣的內容。這個問題相信作過單片機數碼管實驗的同窗立刻就猜到了,沒錯,原理就是利用人眼視覺的暫留性(我是這麼叫的,再專業點就不是我研究的問題了)--當兩個數碼管之間的變化小於100ms(貌似是這麼長時間)時,咱們的眼睛是看不到他們之間的閃爍的。因而,當咱們要顯示1234這4個數時,咱們能夠在1ms時顯示1,25ms時顯示2,50ms時顯示3,75ms時顯示4,而後循環,看起來就是一塊兒顯示1234這4個數了。固然,咱們的硬件能夠作的更快,這個時間間隔能夠根據實際狀況選擇更短。把它翻譯成Verilog語言就有下面的程序段了:spa
//數碼管刷新顯示模塊 always@(posedge div1000hz) begin if(rest) begin seg_count <= 0; an <= 4'b0000; seg <= 8'b0000_0000; end else begin case(seg_count) 0 : begin case(sec1) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN1; end 1 : begin case(sec2) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN2; end 2 : begin case(min1) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN3; end 3 : begin case(min2) 0 : seg <= zero; 1 : seg <= one; 2 : seg <= two; 3 : seg <= three; 4 : seg <= four; 5 : seg <= five; 6 : seg <= six; 7 : seg <= seven; 8 : seg <= eigth; 9 : seg <= nine; endcase an <= AN4; end endcase seg_count <= seg_count + 1; end end
好,如今剩最後一個問題了,那就是時鐘控制部分。熟悉C的同窗會說,那還不簡單,分頻計數,而後整除求餘,將秒、分的十位各位分開不就能夠了嗎?沒錯,這是最簡單的方式,可是,你們千萬不要忽略了咱們最後的代碼是要變成硬件電路的。先不說Xilinx的IDE內嵌的XST綜合器不支持對除號的綜合,要實現除法必需要設計除法器(調用IP或本身設計),就算是設計出來了,一個除法器佔用的硬件資源可能會比咱們剩餘部分電路佔用的資源還多。從這方面出發考慮,咱們要想一想是否是有更好的方法實現它。其實只要咱們稍微思考一下仍是不難的,下面的代碼就能很簡潔的完成這部分的設計:翻譯
//時鐘模塊 always@(posedge div1hz) begin if(rest) begin sec1 <= 0; sec2 <= 0; min1 <= 0; min2 <= 0; end else begin sec1 <= sec1 + 1; if(sec1 == 9) begin sec2 <= sec2 + 1; sec1 <= 0; end if(sec2 == 5 && sec1 == 9) begin min1 <= min1 + 1; sec2 <= 0; end if(min1 == 9) begin min2 <= min2 + 1; min1 <= 0; end if(min2 == 5 && min1 == 9) begin min2 <= 0; end end end
可能咱們會多寫幾行代碼,可是最後設計出來的電路的性能每每會有大的飛躍,這是咱們在作硬件開發時須要時常提醒本身的。設計
好了,以上就是這個DEMO的關鍵部分,其實也是至關基礎的,把它拿出來做爲個人第一篇技術文章與你們分享,有不當之處還請你們指出。下面是完整的程序,中間還將LED顯示加進去了,讓八個LED間隔1s亮滅:rest
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: lwy 5 // 6 // Create Date: 00:33:08 11/09/2013 7 // Design Name: 8 // Module Name: segclk 9 // Project Name: 10 // Target Devices: 11 // Tool versions: 12 // Description:NEXYS3開發板測試程序,用數碼管實現時鐘功能 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 module segclk( 22 clk,//時鐘輸入,100M 23 rest,//時鐘復位信號,BTNU,高電平時復位 24 seg,//數碼管段選 25 an,//數碼管位選 26 led//8個LED 27 ); 28 29 input clk,rest; 30 output reg [7:0] seg; 31 output reg [3:0] an; 32 output reg [7:0] led; 33 34 parameter AN1 = 4'b1110,AN2 = 4'b1101,AN3 = 4'b1011,AN4 = 4'b0111;//數碼管位選定義 35 parameter zero = 8'b0000_0011,one = 8'b1001_1111,two = 8'b0010_0101,three = 8'b0000_1101,four = 8'b1001_1001, 36 five = 8'b0100_1001,six = 8'b0100_0001,seven = 8'b0001_1111,eigth = 8'b0000_0001,nine = 8'b0000_1001;//數碼管段選定義 37 parameter led_on = 8'b1111_1111,led_off = 8'b0000_0000;//LED亮滅定義 38 39 reg [25:0] div1hz_temp;//1秒分頻計數 40 reg [12:0] div1000hz_temp;//數碼管刷新時間分頻計數 41 reg div1hz;//1Hz時鐘 42 reg div1000hz;//數碼管刷新時鐘,頻率爲(100M/2^13)Hz 43 reg led_temp;//LED亮滅 44 45 reg [3:0] sec1;//秒個位 46 reg [3:0] sec2;//秒十位 47 reg [3:0] min1;//分個位 48 reg [3:0] min2;//分十位 49 50 reg [1:0] seg_count;//數碼管位選定位 51 52 //1秒分頻模塊 53 always@(posedge clk) 54 begin 55 div1hz_temp <= div1hz_temp + 1; 56 if(div1hz_temp == 50000000) 57 begin 58 div1hz_temp <= 0; 59 div1hz <= ~div1hz; 60 end 61 end 62 63 //數碼管刷新時間分頻模塊 64 always@(posedge clk) 65 begin 66 div1000hz_temp <= div1000hz_temp + 1; 67 if(div1000hz_temp == 8191) 68 begin 69 div1000hz <= ~div1000hz; 70 end 71 end 72 73 //時鐘模塊 74 always@(posedge div1hz) 75 begin 76 if(rest) 77 begin 78 sec1 <= 0; 79 sec2 <= 0; 80 min1 <= 0; 81 min2 <= 0; 82 end 83 else 84 begin 85 sec1 <= sec1 + 1; 86 if(sec1 == 9) 87 begin 88 sec2 <= sec2 + 1; 89 sec1 <= 0; 90 end 91 if(sec2 == 5 && sec1 == 9) 92 begin 93 min1 <= min1 + 1; 94 sec2 <= 0; 95 end 96 if(min1 == 9) 97 begin 98 min2 <= min2 + 1; 99 min1 <= 0; 100 end 101 if(min2 == 5 && min1 == 9) 102 begin 103 min2 <= 0; 104 end 105 end 106 end 107 108 //LED亮滅控制模塊 109 always@(posedge div1hz) 110 begin 111 led_temp = ~led_temp; 112 if(led_temp) 113 led <= led_on; 114 else 115 led <= led_off; 116 end 117 118 //數碼管刷新顯示模塊 119 always@(posedge div1000hz) 120 begin 121 if(rest) 122 begin 123 seg_count <= 0; 124 an <= 4'b0000; 125 seg <= 8'b0000_0000; 126 end 127 else 128 begin 129 case(seg_count) 130 0 : begin 131 case(sec1) 132 0 : seg <= zero; 133 1 : seg <= one; 134 2 : seg <= two; 135 3 : seg <= three; 136 4 : seg <= four; 137 5 : seg <= five; 138 6 : seg <= six; 139 7 : seg <= seven; 140 8 : seg <= eigth; 141 9 : seg <= nine; 142 endcase 143 an <= AN1; 144 end 145 1 : begin 146 case(sec2) 147 0 : seg <= zero; 148 1 : seg <= one; 149 2 : seg <= two; 150 3 : seg <= three; 151 4 : seg <= four; 152 5 : seg <= five; 153 6 : seg <= six; 154 7 : seg <= seven; 155 8 : seg <= eigth; 156 9 : seg <= nine; 157 endcase 158 an <= AN2; 159 end 160 2 : begin 161 case(min1) 162 0 : seg <= zero; 163 1 : seg <= one; 164 2 : seg <= two; 165 3 : seg <= three; 166 4 : seg <= four; 167 5 : seg <= five; 168 6 : seg <= six; 169 7 : seg <= seven; 170 8 : seg <= eigth; 171 9 : seg <= nine; 172 endcase 173 an <= AN3; 174 end 175 3 : begin 176 case(min2) 177 0 : seg <= zero; 178 1 : seg <= one; 179 2 : seg <= two; 180 3 : seg <= three; 181 4 : seg <= four; 182 5 : seg <= five; 183 6 : seg <= six; 184 7 : seg <= seven; 185 8 : seg <= eigth; 186 9 : seg <= nine; 187 endcase 188 an <= AN4; 189 end 190 endcase 191 seg_count <= seg_count + 1; 192 end 193 end 194 195 endmodule