2022寒假在家一起练——基于iCE40UP5K的FPGA学习平台,利用PWM制作一个音乐播放器(项目2)
1 项目需求及功能
- 利用PWM驱动蜂鸣器以播放歌曲
- 将正在播放的曲目名称通过SPI OLED显示
- 用含有软件消抖的按键切换歌曲
- 呼吸灯指示按键的触发
2 整体功能框图
整体功能框图由图一所示。
图一:整体功能框图
3 操作说明
板卡资源的操作与说明由下图所示(图二)。
图二:操作与说明
4 代码与实现思路
依据功能逐项介绍如何实现。
4.1 利用PWM驱动蜂鸣器以播放歌曲
扩展板上的蜂鸣器属于无源蜂鸣器,其内部不带震荡源,且直流信号无法令发生。用一定频率交流信号(如方波)去驱动它,可使其发出特定频率的声音。
通过歌曲简谱,将歌曲转换为单音音符的排列;每个单音音符通过在单位时间250ms内,以一定频率的方波驱动蜂鸣器实现。
每个音符的频率右下表所示(表一)。
表一:不同音符对应的频率
根据频率和音符的关系,将音符对应的频率值取出来,根据频率值算出分频比。由表一所示的音符频率,建立音符库模块module key_lib。代码如下:
module key_lib (
input wire clk,
input wire rst,
input wire [4:0] key_num,
output reg [23:0] divnum
);
reg [10:0] freq;
always @ * begin
case (key_num)
5'd0 : freq = 11'd1 ;
5'd1 : freq = 11'd262;
5'd2 : freq = 11'd294;
5'd3 : freq = 11'd330;
5'd4 : freq = 11'd349;
5'd5 : freq = 11'd392;
5'd6 : freq = 11'd440;
5'd7 : freq = 11'd494;
5'd8 : freq = 11'd523;
5'd9 : freq = 11'd587;
5'd10 : freq = 11'd659;
5'd11 : freq = 11'd699;
5'd12 : freq = 11'd784;
5'd13 : freq = 11'd880;
5'd14 : freq = 11'd988;
5'd15 : freq = 11'd1050;
5'd16 : freq = 11'd1175;
5'd17 : freq = 11'd1319;
5'd18 : freq = 11'd1397;
5'd19 : freq = 11'd1568;
5'd20 : freq = 11'd1760;
5'd21 : freq = 11'd1976;
default : freq = 11'd1;
endcase
end
always @ (posedge clk, negedge rst) begin
if (!rst)
divnum <= 24'd12_000_000;
else
divnum <= 12_000_000/freq; //每数这么多次响一下
end
endmodule
以250ms为一个音符的周期,每过250ms,计数器加一,开始下一个音符的输出。模块module next_key代码如下:
module next_key (clk, rst, cnt_key, flag_cnt, key_down);
input wire clk;
input wire rst;
input wire flag_cnt;
input wire key_down;
output reg [8:0] cnt_key;
// each key last 1/4 second. 12_000_000 / 4 = 3_000_000. log2(3_000_000)=22;
parameter WIDTH = 22;
parameter CNT = 3000000;
reg [WIDTH-1:0] count;
wire flag;
always @ (posedge clk, negedge rst) begin
if (!rst) count <= 22'd0;
else
if (count >= CNT-1'b1)
count <= 22'd0;
else
count <= count + 1'b1;
end
assign flag = (count == CNT - 1'b1) ? 1'b1 : 1'b0;
always @ (posedge clk, negedge rst, posedge key_down) begin
if (!rst) cnt_key <= 7'd0;
else if (key_down) cnt_key <= 7'd0;
else if (flag_cnt==1'b0) cnt_key <= 7'd0;
else cnt_key <= cnt_key + flag;
end
endmodule
根据三首歌曲的简谱,依次去索引音符库内的音符,决定在每个250ms内输出那个音符。输出为分频系数。三首歌曲简谱以及代码如下(图三、四、五):
图三:我心永恒简谱
图四:爱美丽简谱
图五:百年孤寂简谱
我心永恒模块module heart代码如下:
module heart (clk,rst,cnt_key,key_num, flag_cnt);
input wire clk;
input wire rst;
input wire [8:0] cnt_key;
output reg [4:0] key_num;
output reg flag_cnt;
always @ (posedge clk, negedge rst) begin
if (rst == 1'b0)
key_num <= 5'd0;
else
case (cnt_key)
9'd0 : begin
key_num <= 5'd15; flag_cnt<=1'b1; end
9'd1 : key_num <= 5'd15;
9'd2 : key_num <= 5'd15;
9'd3 : key_num <= 5'd15;
9'd4 : key_num <= 5'd15;
9'd5 : key_num <= 5'd15;
9'd6 : key_num <= 5'd15;
9'd7 : key_num <= 5'd15;
9'd8 : key_num <= 5'd15;
9'd9 : key_num <= 5'd14;
9'd10 : key_num <= 5'd14;
9'd11 : key_num <= 5'd15;
9'd12 : key_num <= 5'd15;
9'd13 : key_num <= 5'd15;
9'd14 : key_num <= 5'd15;
9'd15 : key_num <= 5'd15;
9'd16 : key_num <= 5'd15;
9'd17 : key_num <= 5'd14;
9'd18 : key_num <= 5'd14;
9'd19 : key_num <= 5'd15;
9'd20 : key_num <= 5'd15;
9'd21 : key_num <= 5'd15;
9'd22 : key_num <= 5'd15;
9'd23 : key_num <= 5'd16;
9'd24 : key_num <= 5'd16;
9'd25 : key_num <= 5'd17;
9'd26 : key_num <= 5'd17;
9'd27 : key_num <= 5'd17;
9'd28 : key_num <= 5'd17;
9'd29 : key_num <= 5'd16;
9'd30 : key_num <= 5'd16;
9'd31 : key_num <= 5'd16;
9'd32 : key_num <= 5'd16;
9'd33 : key_num <= 5'd15;
9'd34 : key_num <= 5'd15;
9'd35 : key_num <= 5'd15;
9'd36 : key_num <= 5'd15;
9'd37 : key_num <= 5'd15;
9'd38 : key_num <= 5'd15;
9'd39 : key_num <= 5'd15;
9'd40 : key_num <= 5'd15;
9'd41 : key_num <= 5'd15;
9'd42 : key_num <= 5'd14;
9'd43 : key_num <= 5'd14;
9'd44 : key_num <= 5'd15;
9'd45 : key_num <= 5'd15;
9'd46 : key_num <= 5'd15;
9'd47 : key_num <= 5'd15;
9'd48 : key_num <= 5'd15;
9'd49 : key_num <= 5'd15;
9'd50 : key_num <= 5'd12;
9'd51 : key_num <= 5'd12;
9'd52 : key_num <= 5'd12;
9'd53 : key_num <= 5'd12;
9'd54 : key_num <= 5'd12;
9'd55 : key_num <= 5'd12;
9'd56 : key_num <= 5'd12;
9'd57 : key_num <= 5'd12;
9'd58 : key_num <= 5'd12;
9'd59 : key_num <= 5'd12;
9'd60 : key_num <= 5'd12;
9'd61 : key_num <= 5'd12;
9'd62 : key_num <= 5'd12;
9'd63 : key_num <= 5'd12;
9'd64 : key_num <= 5'd12;
9'd65 : key_num <= 5'd12;
9'd66 : key_num <= 5'd12;
9'd67 : key_num <= 5'd12;
9'd68 : key_num <= 5'd12;
9'd69 : key_num <= 5'd12;
9'd70 : key_num <= 5'd15;
9'd71 : key_num <= 5'd15;
9'd72 : key_num <= 5'd15;
9'd73 : key_num <= 5'd15;
9'd74 : key_num <= 5'd15;
9'd75 : key_num <= 5'd15;
9'd76 : key_num <= 5'd15;
9'd77 : key_num <= 5'd15;
9'd78 : key_num <= 5'd15;
9'd79 : key_num <= 5'd14;
9'd80 : key_num <= 5'd 14 ;
9'd81 : key_num <= 5'd15;
9'd82 : key_num <= 5'd15;
9'd83 : key_num <= 5'd15;
9'd84 : key_num <= 5'd15;
9'd85 : key_num <= 5'd15;
9'd86 : key_num <= 5'd15;
9'd87 : key_num <= 5'd14;
9'd88 : key_num <= 5'd14;
9'd89 : key_num <= 5'd15;
9'd90 : key_num <= 5'd15;
9'd91 : key_num <= 5'd15;
9'd92 : key_num <= 5'd15;
9'd93 : key_num <= 5'd16;
9'd94 : key_num <= 5'd16;
9'd95 : key_num <= 5'd17;
9'd96 : key_num <= 5'd17;
9'd97 : key_num <= 5'd17;
9'd98 : key_num <= 5'd17;
9'd99 : key_num <= 5'd16;
9'd100 : key_num <= 5'd 16 ;
9'd101 : key_num <= 5'd 16 ;
9'd102 : key_num <= 5'd 16 ;
9'd103 : key_num <= 5'd 15 ;
9'd104 : key_num <= 5'd 15 ;
9'd105 : key_num <= 5'd 15 ;
9'd106 : key_num <= 5'd 15 ;
9'd107 : key_num <= 5'd 15 ;
9'd108 : key_num <= 5'd 15 ;
9'd109 : key_num <= 5'd 15 ;
9'd110 : key_num <= 5'd 15 ;
9'd111 : key_num <= 5'd 15 ;
9'd112 : key_num <= 5'd 14 ;
9'd113 : key_num <= 5'd 14 ;
9'd114 : key_num <= 5'd 15 ;
9'd115 : key_num <= 5'd 15 ;
9'd116 : key_num <= 5'd 15 ;
9'd117 : key_num <= 5'd 15 ;
9'd118 : key_num <= 5'd 15 ;
9'd119 : key_num <= 5'd 15 ;
9'd120 : key_num <= 5'd 12 ;
9'd121 : key_num <= 5'd 12 ;
9'd122 : key_num <= 5'd 12 ;
9'd123 : key_num <= 5'd 12 ;
9'd124 : key_num <= 5'd 12 ;
9'd125 : key_num <= 5'd 12 ;
9'd126 : key_num <= 5'd 12 ;
9'd127 : key_num <= 5'd 12 ;
9'd128 : key_num <= 5'd 12 ;
9'd129 : key_num <= 5'd 12 ;
9'd130 : key_num <= 5'd 12 ;
9'd131 : key_num <= 5'd 12 ;
9'd132 : key_num <=5'd 12 ;
9'd133 : key_num <= 5'd 12 ;
9'd134 : key_num <= 5'd 12 ;
9'd135 : key_num <= 5'd 12 ;
9'd136 : key_num <= 5'd 12 ;
9'd137 : key_num <= 5'd 12 ;
9'd138 : key_num <= 5'd 12 ;
9'd139 : key_num <= 5'd 12 ;
9'd140 : key_num <= 5'd15;
9'd141 : key_num <= 5'd15;
9'd142 : key_num <= 5'd15;
9'd143 : key_num <= 5'd15;
9'd144 : key_num <= 5'd15;
9'd145 : key_num <= 5'd15;
9'd146 : key_num <= 5'd15;
9'd147 : key_num <= 5'd15;
9'd148 : key_num <= 5'd16;
9'd149 : key_num <= 5'd16;
9'd150 : key_num <= 5'd16;
9'd151 : key_num <= 5'd16;
9'd152 : key_num <= 5'd16;
9'd153 : key_num <= 5'd16;
9'd154 : key_num <= 5'd16;
9'd155 : key_num <= 5'd16;
9'd156 : key_num <= 5'd12;
9'd157 : key_num <= 5'd12;
9'd158 : key_num <= 5'd19;
9'd159 : key_num <= 5'd19;
9'd160 : key_num <= 5'd19;
9'd161 : key_num <= 5'd19;
9'd162 : key_num <= 5'd18;
9'd163 : key_num <= 5'd18;
9'd164 : key_num <= 5'd17;
9'd165 : key_num <= 5'd17;
9'd166 : key_num <= 5'd16;
9'd167 : key_num <= 5'd16;
9'd168 : key_num <= 5'd16;
9'd169 : key_num <= 5'd16;
9'd170 : key_num <= 5'd17;
9'd171 : key_num <= 5'd17;
9'd172 : key_num <= 5'd18;
9'd173 : key_num <= 5'd18;
9'd174 : key_num <= 5'd17;
9'd175 : key_num <= 5'd17;
9'd176 : key_num <= 5'd17;
9'd177 : key_num <= 5'd17;
9'd178 : key_num <= 5'd16;
9'd179 : key_num <= 5'd16;
9'd180 : key_num <= 5'd15;
9'd181 : key_num <= 5'd15;
9'd182 : key_num <= 5'd14;
9'd183 : key_num <= 5'd14;
9'd184 : key_num <= 5'd15;
9'd185 : key_num <= 5'd15;
9'd186 : key_num <= 5'd15;
9'd187 : key_num <= 5'd15;
9'd188 : key_num <= 5'd14;
9'd189 : key_num <= 5'd14;
9'd190 : key_num <= 5'd13;
9'd191 : key_num <= 5'd13;
9'd192 : key_num <= 5'd13;
9'd193 : key_num <= 5'd13;
9'd194 : key_num <= 5'd13;
9'd195 : key_num <= 5'd13;
9'd196 : key_num <= 5'd13;
9'd197 : key_num <= 5'd13;
9'd198 : key_num <= 5'd12;
9'd199 : key_num <= 5'd12;
9'd200 : key_num <= 5'd12;
9'd201 : key_num <= 5'd12;
9'd202 : key_num <= 5'd12;
9'd203 : key_num <= 5'd12;
9'd204 : key_num <= 5'd12;
9'd205 : key_num <= 5'd12;
9'd206 : key_num <= 5'd15;
9'd207 : key_num <= 5'd15;
9'd208 : key_num <= 5'd15;
9'd209 : key_num <= 5'd15;
9'd210 : key_num <= 5'd15;
9'd211 : key_num <= 5'd15;
9'd212 : key_num <= 5'd15;
9'd213 : key_num <= 5'd15;
9'd214 : key_num <= 5'd16;
9'd215 : key_num <= 5'd16;
9'd216 : key_num <= 5'd16;
9'd217 : key_num <= 5'd16;
9'd218 : key_num <= 5'd16;
9'd219 : key_num <= 5'd16;
9'd220 : key_num <= 5'd16;
9'd221 : key_num <= 5'd16;
9'd222 : key_num <= 5'd12;
9'd223 : key_num <= 5'd12;
9'd224 : key_num <= 5'd19;
9'd225 : key_num <= 5'd19;
9'd226 : key_num <= 5'd19;
9'd227 : key_num <= 5'd19;
9'd228 : key_num <= 5'd18;
9'd229 : key_num <= 5'd18;
9'd230 : key_num <= 5'd17;
9'd231 : key_num <= 5'd17;
9'd232 : key_num <= 5'd16;
9'd233 : key_num <= 5'd16;
9'd234 : key_num <= 5'd16;
9'd235 : key_num <= 5'd16;
9'd236 : key_num <= 5'd17;
9'd237 : key_num <= 5'd17;
9'd238 : key_num <= 5'd18;
9'd239 : key_num <= 5'd18;
9'd240 : key_num <= 5'd17;
9'd241 : key_num <= 5'd17;
9'd242 : key_num <= 5'd17;
9'd243 : key_num <= 5'd17;
9'd244 : key_num <= 5'd16;
9'd245 : key_num <= 5'd16;
9'd246 : key_num <= 5'd15;
9'd247 : key_num <= 5'd15;
9'd248 : key_num <= 5'd14;
9'd249 : key_num <= 5'd14;
9'd250 : key_num <= 5'd15;
9'd251 : key_num <= 5'd15;
9'd252 : key_num <= 5'd15;
9'd253 : key_num <= 5'd15;
9'd254 : key_num <= 5'd14;
9'd255 : key_num <= 5'd14;
9'd256 : key_num <= 5'd14;
9'd257 : key_num <= 5'd14;
9'd258 : key_num <= 5'd15;
9'd259 : key_num <= 5'd15;
9'd260 : key_num <= 5'd15;
9'd261 : key_num <= 5'd15;
9'd262 : key_num <= 5'd16;
9'd263 : key_num <= 5'd16;
9'd264 : key_num <= 5'd17;
9'd265 : key_num <= 5'd17;
9'd266 : key_num <= 5'd17;
9'd267 : key_num <= 5'd17;
9'd268 : key_num <= 5'd16;
9'd269 : key_num <= 5'd16;
9'd270 : key_num <= 5'd16;
9'd271 : key_num <= 5'd16;
9'd272 : key_num <= 5'd15;
9'd273 : key_num <= 5'd15;
9'd274 : key_num <= 5'd15;
9'd275 : key_num <= 5'd15;
9'd276 : key_num <= 5'd15;
9'd277 : key_num <= 5'd15;
9'd278 : key_num <= 5'd15;
9'd279 : key_num <= 5'd15;
9'd280 : key_num <= 5'd15;
9'd281 : key_num <= 5'd15;
9'd282 : key_num <= 5'd15;
9'd283 : key_num <= 5'd15;
9'd284 : key_num <= 5'd15;
9'd285 : key_num <= 5'd15;
9'd286 : key_num <= 5'd16;
9'd287 : key_num <= 5'd16;
9'd288 : key_num <= 5'd0 ;
9'd289 : key_num <= 5'd0 ;
9'd290 : key_num <= 5'd0 ;
9'd291 : key_num <= 5'd0 ;
9'd292 : key_num <= 5'd0 ;
9'd293 : key_num <= 5'd0 ;
9'd294 : key_num <= 5'd0 ;
9'd295 : begin
key_num <= 5'd0 ; flag_cnt <= 0; end
default : key_num <= 5'd0;
endcase
end
endmodule
爱美丽模块module emily代码如下:
module emily (clk,rst,cnt_key,key_num, flag_cnt);
input wire clk;
input wire rst;
input wire [8:0] cnt_key;
output reg [4:0] key_num;
output reg flag_cnt;
always @ (posedge clk, negedge rst) begin
if (rst == 1'b0)
key_num <= 5'd0;
else
case (cnt_key)
9'd0 : begin
key_num <= 5'd0 ; flag_cnt<=1'b1; end
9'd1 : key_num <= 5'd0 ;
9'd2 : key_num <= 5'd0 ;
9'd3 : key_num <= 5'd0 ;
9'd4 : key_num <= 5'd13;
9'd5 : key_num <= 5'd13;
9'd6 : key_num <= 5'd13;
9'd7 : key_num <= 5'd13;
9'd8 : key_num <= 5'd14;
9'd9 : key_num <= 5'd14;
9'd10 : key_num <= 5'd14;
9'd11 : key_num <= 5'd14;
9'd12 : key_num <= 5'd15;
9'd13 : key_num <= 5'd15;
9'd14 : key_num <= 5'd15;
9'd15 : key_num <= 5'd15;
9'd16 : key_num <= 5'd14;
9'd17 : key_num <= 5'd14;
9'd18 : key_num <= 5'd14;
9'd19 : key_num <= 5'd14;
9'd20 : key_num <= 5'd15;
9'd21 : key_num <= 5'd15;
9'd22 : key_num <= 5'd15;
9'd23 : key_num <= 5'd15;
9'd24 : key_num <= 5'd16;
9'd25 : key_num <= 5'd16;
9'd26 : key_num <= 5'd16;
9'd27 : key_num <= 5'd16;
9'd28 : key_num <= 5'd16;
9'd29 : key_num <= 5'd16;
9'd30 : key_num <= 5'd16;
9'd31 : key_num <= 5'd16;
9'd32 : key_num <= 5'd16;
9'd33 : key_num <= 5'd16;
9'd34 : key_num <= 5'd16;
9'd35 : key_num <= 5'd0 ;
9'd36 : key_num <= 5'd16;
9'd37 : key_num <= 5'd16;
9'd38 : key_num <= 5'd16;
9'd39 : key_num <= 5'd16;
9'd40 : key_num <= 5'd16;
9'd41 : key_num <= 5'd16;
9'd42 : key_num <= 5'd16;
9'd43 : key_num <= 5'd16;
9'd44 : key_num <= 5'd16;
9'd45 : key_num <= 5'd16;
9'd46 : key_num <= 5'd15;
9'd47 : key_num <= 5'd15;
9'd48 : key_num <= 5'd14;
9'd49 : key_num <= 5'd14;
9'd50 : key_num <= 5'd14;
9'd51 : key_num <= 5'd14;
9'd52 : key_num <= 5'd14;
9'd53 : key_num <= 5'd14;
9'd54 : key_num <= 5'd14;
9'd55 : key_num <= 5'd14;
9'd56 : key_num <= 5'd14;
9'd57 : key_num <= 5'd14;
9'd58 : key_num <= 5'd14;
9'd59 : key_num <= 5'd14;
9'd60 : key_num <= 5'd13;
9'd61 : key_num <= 5'd13;
9'd62 : key_num <= 5'd13;
9'd63 : key_num <= 5'd13;
9'd64 : key_num <= 5'd13;
9'd65 : key_num <= 5'd13;
9'd66 : key_num <= 5'd13;
9'd67 : key_num <= 5'd13;
9'd68 : key_num <= 5'd13;
9'd69 : key_num <= 5'd13;
9'd70 : key_num <= 5'd13;
9'd71 : key_num <= 5'd13;
9'd72 : key_num <= 5'd16;
9'd73 : key_num <= 5'd16;
9'd74 : key_num <= 5'd16;
9'd75 : key_num <= 5'd16;
9'd76 : key_num <= 5'd16;
9'd77 : key_num <= 5'd16;
9'd78 : key_num <= 5'd16;
9'd79 : key_num <= 5'd16;
9'd80 : key_num <= 5'd16 ;
9'd81 : key_num <= 5'd16;
9'd82 : key_num <= 5'd16;
9'd83 : key_num <= 5'd16;
9'd84 : key_num <= 5'd16;
9'd85 : key_num <= 5'd16;
9'd86 : key_num <= 5'd17;
9'd87 : key_num <= 5'd17;
9'd88 : key_num <= 5'd16;
9'd89 : key_num <= 5'd16;
9'd90 : key_num <= 5'd15;
9'd91 : key_num <= 5'd15;
9'd92 : key_num <= 5'd14;
9'd93 : key_num <= 5'd14;
9'd94 : key_num <= 5'd15;
9'd95 : key_num <= 5'd15;
9'd96 : key_num <= 5'd14;
9'd97 : key_num <= 5'd14;
9'd98 : key_num <= 5'd14;
9'd99 : key_num <= 5'd14;
9'd100 : key_num <= 5'd14;
9'd101 : key_num <= 5'd14;
9'd102 : key_num <= 5'd14;
9'd103 : key_num <= 5'd14;
9'd104 : key_num <= 5'd14;
9'd105 : key_num <= 5'd14;
9'd106 : key_num <= 5'd14;
9'd107 : key_num <= 5'd14;
9'd108 : key_num <= 5'd13;
9'd109 : key_num <= 5'd13;
9'd110 : key_num <= 5'd13;
9'd111 : key_num <= 5'd13;
9'd112 : key_num <= 5'd13;
9'd113 : key_num <= 5'd13;
9'd114 : key_num <= 5'd13;
9'd115 : key_num <= 5'd13;
9'd116 : key_num <= 5'd13;
9'd117 : key_num <= 5'd13;
9'd118 : key_num <= 5'd13;
9'd119 : key_num <= 5'd13;
9'd120 : key_num <= 5'd15;
9'd121 : key_num <= 5'd15;
9'd122 : key_num <= 5'd15;
9'd123 : key_num <= 5'd15;
9'd124 : key_num <= 5'd15;
9'd125 : key_num <= 5'd15;
9'd126 : key_num <= 5'd15;
9'd127 : key_num <= 5'd15;
9'd128 : key_num <= 5'd15;
9'd129 : key_num <= 5'd15;
9'd130 : key_num <= 5'd15;
9'd131 : key_num <= 5'd15;
9'd132 : key_num <=5'd15;
9'd133 : key_num <= 5'd0 ;
9'd134 : key_num <= 5'd15;
9'd135 : key_num <= 5'd15;
9'd136 : key_num <= 5'd15;
9'd137 : key_num <= 5'd15;
9'd138 : key_num <= 5'd15;
9'd139 : key_num <= 5'd15;
9'd140 : key_num <= 5'd15;
9'd141 : key_num <= 5'd15;
9'd142 : key_num <= 5'd14;
9'd143 : key_num <= 5'd14;
9'd144 : key_num <= 5'd10;
9'd145 : key_num <= 5'd10;
9'd146 : key_num <= 5'd10;
9'd147 : key_num <= 5'd10;
9'd148 : key_num <= 5'd10;
9'd149 : key_num <= 5'd10;
9'd150 : key_num <= 5'd10;
9'd151 : key_num <= 5'd10;
9'd152 : key_num <= 5'd10;
9'd153 : key_num <= 5'd10;
9'd154 : key_num <= 5'd10;
9'd155 : key_num <= 5'd0 ;
9'd156 : key_num <= 5'd10;
9'd157 : key_num <= 5'd10;
9'd158 : key_num <= 5'd10;
9'd159 : key_num <= 5'd10;
9'd160 : key_num <= 5'd10;
9'd161 : key_num <= 5'd10;
9'd162 : key_num <= 5'd10;
9'd163 : key_num <= 5'd10;
9'd164 : key_num <= 5'd11;
9'd165 : key_num <= 5'd11;
9'd166 : key_num <= 5'd11;
9'd167 : key_num <= 5'd11;
9'd168 : key_num <= 5'd15;
9'd169 : key_num <= 5'd15;
9'd170 : key_num <= 5'd15;
9'd171 : key_num <= 5'd15;
9'd172 : key_num <= 5'd15;
9'd173 : key_num <= 5'd15;
9'd174 : key_num <= 5'd15;
9'd175 : key_num <= 5'd15;
9'd176 : key_num <= 5'd15;
9'd177 : key_num <= 5'd15;
9'd178 : key_num <= 5'd15;
9'd179 : key_num <= 5'd0 ;
9'd180 : key_num <= 5'd15;
9'd181 : key_num <= 5'd15;
9'd182 : key_num <= 5'd16;
9'd183 : key_num <= 5'd16;
9'd184 : key_num <= 5'd15;
9'd185 : key_num <= 5'd15;
9'd186 : key_num <= 5'd14;
9'd187 : key_num <= 5'd14;
9'd188 : key_num <= 5'd15;
9'd189 : key_num <= 5'd15;
9'd190 : key_num <= 5'd14;
9'd191 : key_num <= 5'd14;
9'd192 : key_num <= 5'd10;
9'd193 : key_num <= 5'd10;
9'd194 : key_num <= 5'd10;
9'd195 : key_num <= 5'd10;
9'd196 : key_num <= 5'd10;
9'd197 : key_num <= 5'd10;
9'd198 : key_num <= 5'd10;
9'd199 : key_num <= 5'd10;
9'd200 : key_num <= 5'd10;
9'd201 : key_num <= 5'd10;
9'd202 : key_num <= 5'd10;
9'd203 : key_num <= 5'd0 ;
9'd204 : key_num <= 5'd10;
9'd205 : key_num <= 5'd10;
9'd206 : key_num <= 5'd10;
9'd207 : key_num <= 5'd10;
9'd208 : key_num <= 5'd11;
9'd209 : key_num <= 5'd11;
9'd210 : key_num <= 5'd11;
9'd211 : key_num <= 5'd11;
9'd212 : key_num <= 5'd10;
9'd213 : key_num <= 5'd10;
9'd214 : key_num <= 5'd10;
9'd215 : key_num <= 5'd10;
9'd216 : key_num <= 5'd9 ;
9'd217 : key_num <= 5'd9 ;
9'd218 : key_num <= 5'd10;
9'd219 : key_num <= 5'd10;
9'd220 : key_num <= 5'd11;
9'd221 : key_num <= 5'd11;
9'd222 : key_num <= 5'd10;
9'd223 : key_num <= 5'd10;
9'd224 : key_num <= 5'd11;
9'd225 : key_num <= 5'd11;
9'd226 : key_num <= 5'd10;
9'd227 : key_num <= 5'd10;
9'd228 : key_num <= 5'd16;
9'd229 : key_num <= 5'd16;
9'd230 : key_num <= 5'd10;
9'd231 : key_num <= 5'd10;
9'd232 : key_num <= 5'd11;
9'd233 : key_num <= 5'd11;
9'd234 : key_num <= 5'd15;
9'd235 : key_num <= 5'd15;
9'd236 : key_num <= 5'd10;
9'd237 : key_num <= 5'd10;
9'd238 : key_num <= 5'd11;
9'd239 : key_num <= 5'd11;
9'd240 : key_num <= 5'd14;
9'd241 : key_num <= 5'd14;
9'd242 : key_num <= 5'd9 ;
9'd243 : key_num <= 5'd9 ;
9'd244 : key_num <= 5'd10;
9'd245 : key_num <= 5'd10;
9'd246 : key_num <= 5'd9 ;
9'd247 : key_num <= 5'd9 ;
9'd248 : key_num <= 5'd10;
9'd249 : key_num <= 5'd10;
9'd250 : key_num <= 5'd9 ;
9'd251 : key_num <= 5'd9 ;
9'd252 : key_num <= 5'd13;
9'd253 : key_num <= 5'd13;
9'd254 : key_num <= 5'd8 ;
9'd255 : key_num <= 5'd8 ;
9'd256 : key_num <= 5'd9 ;
9'd257 : key_num <= 5'd9 ;
9'd258 : key_num <= 5'd10;
9'd259 : key_num <= 5'd10;
9'd260 : key_num <= 5'd9 ;
9'd261 : key_num <= 5'd9 ;
9'd262 : key_num <= 5'd8 ;
9'd263 : key_num <= 5'd8 ;
9'd264 : key_num <= 5'd9 ;
9'd265 : key_num <= 5'd9 ;
9'd266 : key_num <= 5'd10;
9'd267 : key_num <= 5'd10;
9'd268 : key_num <= 5'd11;
9'd269 : key_num <= 5'd11;
9'd270 : key_num <= 5'd10;
9'd271 : key_num <= 5'd10;
9'd272 : key_num <= 5'd11;
9'd273 : key_num <= 5'd11;
9'd274 : key_num <= 5'd10;
9'd275 : key_num <= 5'd10;
9'd276 : key_num <= 5'd16;
9'd277 : key_num <= 5'd16;
9'd278 : key_num <= 5'd10;
9'd279 : key_num <= 5'd10;
9'd280 : key_num <= 5'd11;
9'd281 : key_num <= 5'd11;
9'd282 : key_num <= 5'd15;
9'd283 : key_num <= 5'd15;
9'd284 : key_num <= 5'd10;
9'd285 : key_num <= 5'd10;
9'd286 : key_num <= 5'd11;
9'd287 : key_num <= 5'd11;
9'd288 : key_num <= 5'd14;
9'd289 : key_num <= 5'd14;
9'd290 : key_num <= 5'd9 ;
9'd291 : key_num <= 5'd9 ;
9'd292 : key_num <= 5'd10;
9'd293 : key_num <= 5'd10;
9'd294 : key_num <= 5'd9 ;
9'd291 : key_num <= 5'd9 ;
9'd292 : key_num <= 5'd10;
9'd293 : key_num <= 5'd10;
9'd294 : key_num <= 5'd9 ;
9'd295 : key_num <= 5'd9 ;
9'd296 : key_num <= 5'd13;
9'd297 : key_num <= 5'd13;
9'd298 : key_num <= 5'd8 ;
9'd299 : key_num <= 5'd8 ;
9'd300 : key_num <= 5'd9 ;
9'd301 : key_num <= 5'd9 ;
9'd302 : key_num <= 5'd10;
9'd303 : key_num <= 5'd10;
9'd304 : key_num <= 5'd9;
9'd305 : key_num <= 5'd9;
9'd306 : key_num <= 5'd8 ;
9'd307 : key_num <= 5'd8 ;
9'd308 : key_num <= 5'd15;
9'd309 : key_num <= 5'd15;
9'd310 : key_num <= 5'd10;
9'd311 : key_num <= 5'd10;
9'd312 : key_num <= 5'd11;
9'd313 : key_num <= 5'd11;
9'd314 : key_num <= 5'd10;
9'd315 : key_num <= 5'd10;
9'd316 : key_num <= 5'd11;
9'd317 : key_num <= 5'd11;
9'd318 : key_num <= 5'd10;
9'd319 : key_num <= 5'd10;
9'd320 : key_num <= 5'd15;
9'd321 : key_num <= 5'd15;
9'd322 : key_num <= 5'd10;
9'd323 : key_num <= 5'd10;
9'd324 : key_num <= 5'd11;
9'd325 : key_num <= 5'd11;
9'd326 : key_num <= 5'd10;
9'd327 : key_num <= 5'd10;
9'd328 : key_num <= 5'd11;
9'd329 : key_num <= 5'd11;
9'd330 : key_num <= 5'd9 ;
9'd331 : key_num <= 5'd9 ;
9'd332 : key_num <= 5'd12;
9'd333 : key_num <= 5'd12;
9'd334 : key_num <= 5'd9 ;
9'd335 : key_num <= 5'd9 ;
9'd336 : key_num <= 5'd10;
9'd337 : key_num <= 5'd10;
9'd338 : key_num <= 5'd9 ;
9'd339 : key_num <= 5'd9 ;
9'd340 : key_num <= 5'd10;
9'd341 : key_num <= 5'd10;
9'd342 : key_num <= 5'd9 ;
9'd343 : key_num <= 5'd9 ;
9'd344 : key_num <= 5'd10;
9'd345 : key_num <= 5'd10;
9'd346 : key_num <= 5'd8 ;
9'd347 : key_num <= 5'd8 ;
9'd348 : key_num <= 5'd9 ;
9'd349 : key_num <= 5'd9 ;
9'd350 : key_num <= 5'd10;
9'd351 : key_num <= 5'd10;
9'd352 : key_num <= 5'd9 ;
9'd353 : key_num <= 5'd9 ;
9'd354 : key_num <= 5'd8 ;
9'd355 : key_num <= 5'd8 ;
9'd356 : key_num <= 5'd15;
9'd357 : key_num <= 5'd15;
9'd358 : key_num <= 5'd10;
9'd359 : key_num <= 5'd10;
9'd360 : key_num <= 5'd11;
9'd361 : key_num <= 5'd11;
9'd362 : key_num <= 5'd10;
9'd363 : key_num <= 5'd10;
9'd364 : key_num <= 5'd11;
9'd365 : key_num <= 5'd11;
9'd366 : key_num <= 5'd10;
9'd367 : key_num <= 5'd10;
9'd368 : key_num <= 5'd15;
9'd369 : key_num <= 5'd15;
9'd370 : key_num <= 5'd10;
9'd371 : key_num <= 5'd10;
9'd372 : key_num <= 5'd11;
9'd373 : key_num <= 5'd11;
9'd374 : key_num <= 5'd10;
9'd375 : key_num <= 5'd10;
9'd376 : key_num <= 5'd11;
9'd377 : key_num <= 5'd11;
9'd378 : key_num <= 5'd9 ;
9'd379 : key_num <= 5'd9 ;
9'd380 : key_num <= 5'd12;
9'd381 : key_num <= 5'd12;
9'd382 : key_num <= 5'd9 ;
9'd383 : key_num <= 5'd9 ;
9'd384 : key_num <= 5'd10;
9'd385 : key_num <= 5'd10;
9'd386 : key_num <= 5'd9 ;
9'd387 : key_num <= 5'd9 ;
9'd388 : key_num <= 5'd10;
9'd389 : key_num <= 5'd10;
9'd390 : key_num <= 5'd9 ;
9'd391 : key_num <= 5'd9 ;
9'd392 : key_num <= 5'd10;
9'd393 : key_num <= 5'd10;
9'd394 : key_num <= 5'd8 ;
9'd395 : key_num <= 5'd8 ;
9'd396 : key_num <= 5'd9 ;
9'd397 : key_num <= 5'd9 ;
9'd398 : key_num <= 5'd10;
9'd399 : key_num <= 5'd10;
9'd400 : key_num <= 5'd9 ;
9'd401 : key_num <= 5'd9 ;
9'd402 : key_num <= 5'd8 ;
9'd403 : key_num <= 5'd8 ;
9'd404 : key_num <= 5'd0 ;
9'd405 : key_num <= 5'd0 ;
9'd406 : key_num <= 5'd0 ;
9'd407 : key_num <= 5'd0 ;
9'd408 : key_num <= 5'd0 ;
9'd409 : key_num <= 5'd0 ;
9'd410 : key_num <= 5'd0 ;
9'd411 : begin
key_num <= 5'd0 ; flag_cnt <= 0; end
default : key_num <= 5'd0;
endcase
end
endmodule
百年孤寂模块module lonely代码如下:
module lonely (clk,rst,cnt_key,key_num, flag_cnt);
input wire clk;
input wire rst;
input wire [8:0] cnt_key;
output reg [4:0] key_num;
output reg flag_cnt;
always @ (posedge clk, negedge rst) begin
if (rst == 1'b0)
key_num <= 5'd0;
else
case (cnt_key)
9'd0 : begin
key_num <= 5'd14; flag_cnt<=1'b1; end
9'd1 : key_num <= 5'd14;
9'd2 : key_num <= 5'd14;
9'd3 : key_num <= 5'd14;
9'd4 : key_num <= 5'd14;
9'd5 : key_num <= 5'd14;
9'd6 : key_num <= 5'd14;
9'd7 : key_num <= 5'd14;
9'd8 : key_num <= 5'd9 ;
9'd9 : key_num <= 5'd8 ;
9'd10 : key_num <= 5'd6 ;
9'd11 : key_num <= 5'd6 ;
9'd12 : key_num <= 5'd8 ;
9'd13 : key_num <= 5'd8 ;
9'd14 : key_num <= 5'd0 ;
9'd15 : key_num <= 5'd0 ;
9'd16 : key_num <= 5'd6 ;
9'd17 : key_num <= 5'd6 ;
9'd18 : key_num <= 5'd12;
9'd19 : key_num <= 5'd12;
9'd20 : key_num <= 5'd11;
9'd21 : key_num <= 5'd11;
9'd22 : key_num <= 5'd12;
9'd23 : key_num <= 5'd12;
9'd24 : key_num <= 5'd11;
9'd25 : key_num <= 5'd11;
9'd26 : key_num <= 5'd0 ;
9'd27 : key_num <= 5'd0 ;
9'd28 : key_num <= 5'd0 ;
9'd29 : key_num <= 5'd0 ;
9'd30 : key_num <= 5'd0 ;
9'd31 : key_num <= 5'd0 ;
9'd32 : key_num <= 5'd0 ;
9'd33 : key_num <= 5'd0 ;
9'd34 : key_num <= 5'd0 ;
9'd35 : key_num <= 5'd0 ;
9'd36 : key_num <= 5'd9 ;
9'd37 : key_num <= 5'd9 ;
9'd38 : key_num <= 5'd10;
9'd39 : key_num <= 5'd10;
9'd40 : key_num <= 5'd6 ;
9'd41 : key_num <= 5'd6 ;
9'd42 : key_num <= 5'd7 ;
9'd43 : key_num <= 5'd7 ;
9'd44 : key_num <= 5'd8 ;
9'd45 : key_num <= 5'd8 ;
9'd46 : key_num <= 5'd8 ;
9'd47 : key_num <= 5'd8 ;
9'd48 : key_num <= 5'd8 ;
9'd49 : key_num <= 5'd9 ;
9'd50 : key_num <= 5'd9 ;
9'd51 : key_num <= 5'd7 ;
9'd52 : key_num <= 5'd7 ;
9'd53 : key_num <= 5'd7 ;
9'd54 : key_num <= 5'd7 ;
9'd55 : key_num <= 5'd7 ;
9'd56 : key_num <= 5'd7 ;
9'd57 : key_num <= 5'd7 ;
9'd58 : key_num <= 5'd7 ;
9'd59 : key_num <= 5'd7 ;
9'd60 : key_num <= 5'd7 ;
9'd61 : key_num <= 5'd7 ;
9'd62 : key_num <= 5'd7 ;
9'd63 : key_num <= 5'd0 ;
9'd64 : key_num <= 5'd0 ;
9'd65 : key_num <= 5'd0 ;
9'd66 : key_num <= 5'd0 ;
9'd67 : key_num <= 5'd14;
9'd68 : key_num <= 5'd14;
9'd69 : key_num <= 5'd14;
9'd70 : key_num <= 5'd14;
9'd71 : key_num <= 5'd14;
9'd72 : key_num <= 5'd14;
9'd73 : key_num <= 5'd14;
9'd74 : key_num <= 5'd14;
9'd75 : key_num <= 5'd9 ;
9'd76 : key_num <= 5'd8 ;
9'd77 : key_num <= 5'd7 ;
9'd78 : key_num <= 5'd7 ;
9'd79 : key_num <= 5'd8 ;
9'd80 : key_num <= 5'd8 ;
9'd81 : key_num <= 5'd0 ;
9'd82 : key_num <= 5'd0 ;
9'd83 : key_num <= 5'd6 ;
9'd84 : key_num <= 5'd6 ;
9'd85 : key_num <= 5'd12;
9'd86 : key_num <= 5'd12;
9'd87 : key_num <= 5'd11;
9'd88 : key_num <= 5'd11;
9'd89 : key_num <= 5'd12;
9'd90 : key_num <= 5'd12;
9'd91 : key_num <= 5'd11;
9'd92 : key_num <= 5'd11;
9'd93 : key_num <= 5'd0 ;
9'd94 : key_num <= 5'd0 ;
9'd95 : key_num <= 5'd0 ;
9'd96 : key_num <= 5'd0 ;
9'd97 : key_num <= 5'd0 ;
9'd98 : key_num <= 5'd0 ;
9'd99 : key_num <= 5'd0 ;
9'd100 : key_num <= 5'd0 ;
9'd101 : key_num <= 5'd0 ;
9'd102 : key_num <= 5'd0 ;
9'd103 : key_num <= 5'd9 ;
9'd104 : key_num <= 5'd9 ;
9'd105 : key_num <= 5'd10;
9'd106 : key_num <= 5'd10;
9'd107 : key_num <= 5'd6 ;
9'd108 : key_num <= 5'd6 ;
9'd109 : key_num <= 5'd7 ;
9'd110 : key_num <= 5'd7 ;
9'd111 : key_num <= 5'd8 ;
9'd112 : key_num <= 5'd8 ;
9'd113 : key_num <= 5'd8 ;
9'd114 : key_num <= 5'd8 ;
9'd115 : key_num <= 5'd8 ;
9'd116 : key_num <= 5'd9 ;
9'd117 : key_num <= 5'd9 ;
9'd118 : key_num <= 5'd10;
9'd119 : key_num <= 5'd10;
9'd120 : key_num <= 5'd10;
9'd121 : key_num <= 5'd10;
9'd122 : key_num <= 5'd10;
9'd123 : key_num <= 5'd10;
9'd124 : key_num <= 5'd10;
9'd125 : key_num <= 5'd10;
9'd126 : key_num <= 5'd10;
9'd127 : key_num <= 5'd10;
9'd128 : key_num <= 5'd10;
9'd129 : key_num <= 5'd10;
9'd130 : key_num <= 5'd10 ;
9'd131 : key_num <= 5'd11;
9'd132 : key_num <=5'd10;
9'd133 : key_num <= 5'd11;
9'd134 : key_num <= 5'd12;
9'd135 : key_num <= 5'd12;
9'd136 : key_num <= 5'd8 ;
9'd137 : key_num <= 5'd8 ;
9'd138 : key_num <= 5'd8 ;
9'd139 : key_num <= 5'd8 ;
9'd140 : key_num <= 5'd8 ;
9'd141 : key_num <= 5'd8 ;
9'd142 : key_num <= 5'd12;
9'd143 : key_num <= 5'd12;
9'd144 : key_num <= 5'd8 ;
9'd145 : key_num <= 5'd8 ;
9'd146 : key_num <= 5'd8 ;
9'd147 : key_num <= 5'd8 ;
9'd148 : key_num <= 5'd8 ;
9'd149 : key_num <= 5'd8 ;
9'd150 : key_num <= 5'd15;
9'd151 : key_num <= 5'd14;
9'd152 : key_num <= 5'd14;
9'd153 : key_num <= 5'd12;
9'd154 : key_num <= 5'd12;
9'd155 : key_num <= 5'd12;
9'd156 : key_num <= 5'd12;
9'd157 : key_num <= 5'd11;
9'd158 : key_num <= 5'd11;
9'd159 : key_num <= 5'd10;
9'd160 : key_num <= 5'd10;
9'd161 : key_num <= 5'd10;
9'd162 : key_num <= 5'd10;
9'd163 : key_num <= 5'd10;
9'd164 : key_num <= 5'd10;
9'd165 : key_num <= 5'd10;
9'd166 : key_num <= 5'd10;
9'd167 : key_num <= 5'd0 ;
9'd168 : key_num <= 5'd0 ;
9'd169 : key_num <= 5'd0 ;
9'd170 : key_num <= 5'd0 ;
9'd171 : key_num <= 5'd11;
9'd172 : key_num <= 5'd11;
9'd173 : key_num <= 5'd10;
9'd174 : key_num <= 5'd10;
9'd175 : key_num <= 5'd11;
9'd176 : key_num <= 5'd11;
9'd177 : key_num <= 5'd10;
9'd178 : key_num <= 5'd10;
9'd179 : key_num <= 5'd10;
9'd180 : key_num <= 5'd10;
9'd181 : key_num <= 5'd6 ;
9'd182 : key_num <= 5'd6 ;
9'd183 : key_num <= 5'd7 ;
9'd184 : key_num <= 5'd7 ;
9'd185 : key_num <= 5'd7 ;
9'd186 : key_num <= 5'd7 ;
9'd187 : key_num <= 5'd7 ;
9'd188 : key_num <= 5'd7 ;
9'd189 : key_num <= 5'd7 ;
9'd190 : key_num <= 5'd7 ;
9'd191 : key_num <= 5'd7 ;
9'd192 : key_num <= 5'd7 ;
9'd193 : key_num <= 5'd7 ;
9'd194 : key_num <= 5'd7 ;
9'd195 : key_num <= 5'd0 ;
9'd196 : key_num <= 5'd0 ;
9'd197 : key_num <= 5'd0 ;
9'd198 : begin
key_num <= 5'd0 ; flag_cnt <= 0; end
default : key_num <= 5'd0;
endcase
end
endmodule
通过分频数后,产生相应的波形。模块module drop_da_beat代码如下:
module drop_da_beat (
input wire clk,
input wire rst,
input wire [23:0] divnum,
output reg drive
);
reg [23:0] count;
always @ (posedge clk, negedge rst) begin
if (!rst) count <= 24'd0;
else if (count >= divnum - 1'b1) count <= 24'd0;
else count <= count + 1'b1;
end
always @ (posedge clk, negedge rst) begin
if (!rst) drive <= 1'b0;
else if (count < divnum[23:1]) drive <= 1'b0;
else drive <= 1'b1;
end
endmodule
引入2位寄存器[1:0]flag,通过按键控制flag,决定当前播放的歌曲。连接上述模块,形成音频部分的顶层模块module auditory_top。代码如下:
module auditory_top (
input wire clk,
input wire rst,
input wire key_down,
input wire [1:0] flag,
output wire drive
);
reg flag_cnt;
reg [4:0] key_num;
wire [8:0] cnt_key;
wire [4:0] key_num_lonely, key_num_heart, key_num_emily;
wire [23:0] divnum;
wire flag_cnt_lonely, flag_cnt_heart, flag_cnt_emily;
always@(posedge clk, negedge rst) begin
if (!rst) begin
flag_cnt <= flag_cnt_heart;
key_num <= key_num_heart;
end
else
case(flag)
2'd0: begin flag_cnt <= flag_cnt_heart; key_num <= key_num_heart; end
2'd1: begin flag_cnt <= flag_cnt_emily; key_num <= key_num_emily; end
2'd2: begin flag_cnt <= flag_cnt_lonely; key_num <= key_num_lonely; end
default: begin flag_cnt <= flag_cnt_heart; key_num <= key_num_heart; end
endcase
end
next_key next_key_inst(
.clk (clk),
.rst (rst),
.flag_cnt (flag_cnt),
.cnt_key (cnt_key),
.key_down (key_down)
);
heart music_u1(
.clk (clk),
.rst (rst),
.cnt_key (cnt_key),
.key_num (key_num_heart),
.flag_cnt (flag_cnt_heart)
);
emily music_u2(
.clk (clk),
.rst (rst),
.cnt_key (cnt_key),
.key_num (key_num_emily),
.flag_cnt (flag_cnt_emily)
);
lonely music_u3(
.clk (clk),
.rst (rst),
.cnt_key (cnt_key),
.key_num (key_num_lonely),
.flag_cnt (flag_cnt_lonely)
);
key_lib key_lib_inst(
.clk (clk),
.rst (rst),
.key_num (key_num),
.divnum (divnum)
);
drop_da_beat drop_da_beat_inst(
.clk (clk),
.rst (rst),
.divnum (divnum),
.drive (drive)
);
endmodule
4.2 将正在播放的曲目名称通过SPI OLED显示
由于OLED这一个从设备,#CS始终接地拉低。所以此SPI OLED的模块接口为6-pin SPI。
包含7个工作状态:INIT, IDLE, MAIN, SCAN, CHINESE, WRITE, DELAY。
通过汉字字模转换工具,建立汉字字符库。字模转换工具设定如下图所示(图六)。
图六:OLED取模设置
通过与音频模块共享的flag信号,完成对应歌曲名称字符的选择与输出。
模块module OLED12864代码如下:
module OLED12864
(
input clk, //12MHz系统时钟
input rst_n, //系统复位,低有效
input key_up,
input key_down,
output reg [1:0] flag,
output reg oled_csn, //OLCD液晶屏使能
output reg oled_rst, //OLCD液晶屏复位
output reg oled_dcn, //OLCD数据指令控制
output reg oled_clk, //OLCD时钟信号
output reg oled_dat //OLCD数据信号
);
localparam INIT_DEPTH = 16'd23; //LCD初始化的命令的数量
localparam IDLE = 7'h1, MAIN = 7'h2, INIT = 7'h4, SCAN = 7'h8, WRITE = 7'h10, DELAY = 7'h20,CHINESE=7'h40;
localparam HIGH = 1'b1, LOW = 1'b0;
localparam DATA = 1'b1, CMD = 1'b0;
reg [7:0] cmd [22:0];
reg [39:0] mem [122:0];
reg [127:0] mem_hanzi[16:0];
reg [1:0] flag;
reg [7:0] mem_hanzi_num;
reg [7:0] y_p, x_ph, x_pl;
reg [(8*21-1):0] char;
reg [7:0] num, char_reg;
reg [4:0] cnt_main, cnt_init, cnt_scan, cnt_write;
reg [5:0] cnt_chinese;
reg [15:0] num_delay, cnt_delay, cnt;
reg [6:0] state, state_back;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;cnt_chinese <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= IDLE; state_back <= IDLE;
end else begin
case(state)
IDLE:begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;mem_hanzi_num<=8'd0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= MAIN; state_back <= MAIN;
end
MAIN:begin
if(cnt_main >= 5'd16) cnt_main <= 5'd13;//接下来执行空操作,实现数据只刷新一次
else cnt_main <= cnt_main + 1'b1;
case(cnt_main) //MAIN状态
5'd0 : begin state <= INIT; end
5'd 1: begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 5: begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 6: begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 7: begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 8: begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd 9: begin y_p <= 8'hb1; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd0 ; state <= CHINESE; end
5'd10: begin y_p <= 8'hb1; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd2 ; state <= CHINESE; end
5'd11 : begin y_p <= 8'hb1; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd4 ; state <= CHINESE; end
5'd12: begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd6 ; state <= CHINESE; end
5'd13: begin y_p <= 8'hb4; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd8 ; state <= CHINESE; end
5'd14: begin y_p <= 8'hb4; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd10; state <= CHINESE; end
5'd15: begin y_p <= 8'hb4; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd12; state <= CHINESE; end
5'd16: begin y_p <= 8'hb4; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd14; state <= CHINESE; end
//default: state <= IDLE; //如果你需要动态刷新一些信息,此行应该取消注释
endcase
end
INIT:begin //初始化状态
case(cnt_init)
5'd0: begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end //复位有效
5'd1: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end //延时大于3us
5'd2: begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end //复位恢复
5'd3: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end //延时大于220us
5'd4: begin
if(cnt>=INIT_DEPTH) begin //当25条指令及数据发出后,配置完成
cnt <= 1'b0;
cnt_init <= cnt_init + 1'b1;
end else begin
cnt <= cnt + 1'b1; num_delay <= 16'd5;
oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
end
end
5'd5: begin cnt_init <= 1'b0; state <= MAIN; end //初始化完成,返回MAIN状态
default: state <= IDLE;
endcase
end
SCAN:begin //刷屏状态,从RAM中读取数据刷屏
if(cnt_scan == 5'd11) begin
if(num) cnt_scan <= 5'd3;
else cnt_scan <= cnt_scan + 1'b1;
end
else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
else cnt_scan <= cnt_scan + 1'b1;
case(cnt_scan)
5'd 0: begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end //定位列页地址
5'd 1: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end //定位行地址低位
5'd 2: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end //定位行地址高位
5'd 3: begin num <= num - 1'b1;end
5'd 4: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 5: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 6: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 7: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
5'd 8: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
5'd 9: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
5'd10: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
5'd11: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
5'd12: begin state <= MAIN; end
default: state <= IDLE;
endcase
end
CHINESE:begin //显示汉字
if(cnt_chinese == 6'd38) cnt_chinese <= 1'b0;
else cnt_chinese <= cnt_chinese+1'b1;
case(cnt_chinese)
6'd 0: begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= CHINESE; end //定位列页地址
6'd 1: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= CHINESE; end //定位行地址低位
6'd 2: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= CHINESE; end //定位行地址高位
6'd3 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][127:120]; state <= WRITE; state_back <= CHINESE; end
6'd4 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][119:112]; state <= WRITE; state_back <= CHINESE; end
6'd5 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][111:104]; state <= WRITE; state_back <= CHINESE; end
6'd6 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][103:96] ; state <= WRITE; state_back <= CHINESE; end
6'd7 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][95:88] ; state <= WRITE; state_back <= CHINESE; end
6'd8 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][87:80] ; state <= WRITE; state_back <= CHINESE; end
6'd9 : begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][79:72] ; state <= WRITE; state_back <= CHINESE; end
6'd10: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][71:64] ; state <= WRITE; state_back <= CHINESE; end
6'd11: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][63:56]; state <= WRITE; state_back <= CHINESE; end
6'd12: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][55:48]; state <= WRITE; state_back <= CHINESE; end
6'd13: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][47:40]; state <= WRITE; state_back <= CHINESE; end
6'd14: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][39:32]; state <= WRITE; state_back <= CHINESE; end
6'd15: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][31:24]; state <= WRITE; state_back <= CHINESE; end
6'd16: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][23:16]; state <= WRITE; state_back <= CHINESE; end
6'd17: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][15: 8]; state <= WRITE; state_back <= CHINESE; end
6'd18: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num][ 7: 0]; state <= WRITE; state_back <= CHINESE; end
6'd19: begin oled_dcn <= CMD; char_reg <= y_p+1; state <= WRITE; state_back <= CHINESE; end //定位列页地址
6'd20: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= CHINESE; end //定位行地址低位
6'd21: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= CHINESE; end //定位行地址高位
6'd22: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][127:120]; state <= WRITE; state_back <= CHINESE; end
6'd23: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][119:112]; state <= WRITE; state_back <= CHINESE; end
6'd24: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][111:104]; state <= WRITE; state_back <= CHINESE; end
6'd25: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][103:96] ; state <= WRITE; state_back <= CHINESE; end
6'd26: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][95:88] ; state <= WRITE; state_back <= CHINESE; end
6'd27: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][87:80] ; state <= WRITE; state_back <= CHINESE; end
6'd28: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][79:72] ; state <= WRITE; state_back <= CHINESE; end
6'd29: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][71:64] ; state <= WRITE; state_back <= CHINESE; end
6'd30: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][63:56]; state <= WRITE; state_back <= CHINESE; end
6'd31: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][55:48]; state <= WRITE; state_back <= CHINESE; end
6'd32: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][47:40]; state <= WRITE; state_back <= CHINESE; end
6'd33: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][39:32]; state <= WRITE; state_back <= CHINESE; end
6'd34: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][31:24]; state <= WRITE; state_back <= CHINESE; end
6'd35: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][23:16]; state <= WRITE; state_back <= CHINESE; end
6'd36: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][15: 8]; state <= WRITE; state_back <= CHINESE; end
6'd37: begin oled_dcn <= DATA; char_reg <= mem_hanzi[mem_hanzi_num+1][ 7: 0]; state <= WRITE; state_back <= CHINESE; end
6'd38: begin state <= MAIN; end
default: state <= IDLE;
endcase
end
WRITE:begin //WRITE状态,将数据按照SPI时序发送给屏幕
if(cnt_write >= 5'd17) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
5'd 0: begin oled_csn <= LOW; end //9位数据最高位为命令数据控制位
5'd 1: begin oled_clk <= LOW; oled_dat <= char_reg[7]; end //先发高位数据
5'd 2: begin oled_clk <= HIGH; end
5'd 3: begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
5'd 4: begin oled_clk <= HIGH; end
5'd 5: begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
5'd 6: begin oled_clk <= HIGH; end
5'd 7: begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
5'd 8: begin oled_clk <= HIGH; end
5'd 9: begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
5'd10: begin oled_clk <= HIGH; end
5'd11: begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
5'd12: begin oled_clk <= HIGH; end
5'd13: begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
5'd14: begin oled_clk <= HIGH; end
5'd15: begin oled_clk <= LOW; oled_dat <= char_reg[0]; end //后发低位数据
5'd16: begin oled_clk <= HIGH; end
5'd17: begin oled_csn <= HIGH; state <= DELAY; end //
default: state <= IDLE;
endcase
end
DELAY:begin //延时状态
if(cnt_delay >= num_delay) begin
cnt_delay <= 16'd0; state <= state_back;
end else cnt_delay <= cnt_delay + 1'b1;
end
default:state <= IDLE;
endcase
end
end
//OLED配置指令数据
always@(posedge rst_n)
begin
cmd[0 ] = {8'hae};
cmd[1 ] = {8'hd5};
cmd[2 ] = {8'h80};
cmd[3 ] = {8'ha8};
cmd[4 ] = {8'h3f};
cmd[5 ] = {8'hd3};
cmd[6 ] = {8'h00};
cmd[7 ] = {8'h40};
cmd[8 ] = {8'h8d};
cmd[9 ] = {8'h14};
cmd[10] = {8'h20};
cmd[11] = {8'h02};
cmd[12] = {8'hc8};
cmd[13] = {8'ha1};
cmd[14] = {8'hda};
cmd[15] = {8'h12};
cmd[16] = {8'h81};
cmd[17] = {8'hcf};
cmd[18] = {8'hd9};
cmd[19] = {8'hf1};
cmd[20] = {8'hdb};
cmd[21] = {8'h40};
cmd[22] = {8'haf};
end
//5*8点阵字库数据
always@(posedge rst_n)
begin
mem[ 0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E}; // 48 0
mem[ 1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00}; // 49 1
mem[ 2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46}; // 50 2
mem[ 3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31}; // 51 3
mem[ 4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10}; // 52 4
mem[ 5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39}; // 53 5
mem[ 6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30}; // 54 6
mem[ 7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03}; // 55 7
mem[ 8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36}; // 56 8
mem[ 9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E}; // 57 9
mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C}; // 65 A
mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36}; // 66 B
mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22}; // 67 C
mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C}; // 68 D
mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41}; // 69 E
mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01}; // 70 F
mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00}; // 32 sp
mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00}; // 33 !
mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00}; // 34
mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14}; // 35 #
mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12}; // 36 $
mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23}; // 37 %
mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50}; // 38 &
mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00}; // 39 '
mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00}; // 40 (
mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00}; // 41 )
mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14}; // 42 *
mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08}; // 43 +
mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00}; // 44 ,
mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08}; // 45 -
mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00}; // 46 .
mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02}; // 47 /
mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E}; // 48 0
mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00}; // 49 1
mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46}; // 50 2
mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31}; // 51 3
mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10}; // 52 4
mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39}; // 53 5
mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30}; // 54 6
mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03}; // 55 7
mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36}; // 56 8
mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E}; // 57 9
mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00}; // 58 :
mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00}; // 59 ;
mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00}; // 60 <
mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14}; // 61 =
mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08}; // 62 >
mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06}; // 63 ?
mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E}; // 64 @
mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C}; // 65 A
mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36}; // 66 B
mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22}; // 67 C
mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C}; // 68 D
mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41}; // 69 E
mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01}; // 70 F
mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A}; // 71 G
mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F}; // 72 H
mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00}; // 73 I
mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01}; // 74 J
mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41}; // 75 K
mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40}; // 76 L
mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F}; // 77 M
mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F}; // 78 N
mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E}; // 79 O
mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06}; // 80 P
mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E}; // 81 Q
mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46}; // 82 R
mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31}; // 83 S
mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01}; // 84 T
mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F}; // 85 U
mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F}; // 86 V
mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F}; // 87 W
mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63}; // 88 X
mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07}; // 89 Y
mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43}; // 90 Z
mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00}; // 91 [
mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55}; // 92 .
mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00}; // 93 ]
mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04}; // 94 ^
mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40}; // 95 _
mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00}; // 96 '
mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78}; // 97 a
mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38}; // 98 b
mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20}; // 99 c
mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F}; // 100 d
mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18}; // 101 e
mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02}; // 102 f
mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C}; // 103 g
mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78}; // 104 h
mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00}; // 105 i
mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00}; // 106 j
mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00}; // 107 k
mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00}; // 108 l
mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78}; // 109 m
mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78}; // 110 n
mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38}; // 111 o
mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18}; // 112 p
mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC}; // 113 q
mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08}; // 114 r
mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20}; // 115 s
mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20}; // 116 t
mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C}; // 117 u
mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C}; // 118 v
mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C}; // 119 w
mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44}; // 120 x
mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C}; // 121 y
mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44}; // 122 z
end
//16*16 汉字
always@(posedge rst_n, posedge key_up)
begin
mem_hanzi[ 0 ] = {8'h00,8'h02,8'h02,8'hC2,8'h02,8'h02,8'h02,8'hFE,8'h82,8'h82,8'h82,8'h82,8'h82,8'h02,8'h00,8'h00};
mem_hanzi[ 1 ] = {8'h40,8'h40,8'h40,8'h7F,8'h40,8'h40,8'h40,8'h7F,8'h40,8'h40,8'h40,8'h40,8'h40,8'h40,8'h40,8'h00};//正0
mem_hanzi[ 2 ] = {8'h08,8'h08,8'h88,8'hC8,8'h38,8'h0C,8'h0B,8'h08,8'h08,8'hE8,8'h08,8'h08,8'h08,8'h08,8'h08,8'h00};
mem_hanzi[ 3 ] = {8'h02,8'h01,8'h00,8'hFF,8'h40,8'h41,8'h41,8'h41,8'h41,8'h7F,8'h41,8'h41,8'h41,8'h41,8'h40,8'h00};//在1
mem_hanzi[ 4 ] = {8'h10,8'h10,8'hFF,8'h10,8'h90,8'h82,8'h56,8'h3A,8'h12,8'h7F,8'h11,8'h39,8'h55,8'h90,8'h80,8'h00};
mem_hanzi[ 5 ] = {8'h42,8'h82,8'h7F,8'h01,8'h00,8'h00,8'hFF,8'h49,8'h49,8'h7F,8'h49,8'h49,8'hFF,8'h00,8'h00,8'h00};//播2
mem_hanzi[ 6 ] = {8'h08,8'h08,8'hF9,8'h4A,8'h48,8'hC8,8'h48,8'h20,8'hD8,8'h17,8'h10,8'h10,8'hF0,8'h10,8'h10,8'h00};
mem_hanzi[ 7 ] = {8'h40,8'h30,8'h0F,8'h20,8'h40,8'h3F,8'h80,8'h40,8'h21,8'h16,8'h08,8'h16,8'h21,8'h40,8'h80,8'h00};//放3
case(flag)
2'd0: begin
mem_hanzi[ 8 ] = {8'h20,8'h24,8'h24,8'h24,8'hFE,8'h23,8'h22,8'h20,8'h20,8'hFF,8'h20,8'h22,8'h2C,8'hA0,8'h20,8'h00};
mem_hanzi[ 9 ] = {8'h00,8'h08,8'h48,8'h84,8'h7F,8'h02,8'h41,8'h40,8'h20,8'h13,8'h0C,8'h14,8'h22,8'h41,8'hF8,8'h00};//我0
mem_hanzi[ 10 ] = {8'h00,8'h00,8'h80,8'h00,8'h00,8'hE0,8'h02,8'h04,8'h18,8'h00,8'h00,8'h00,8'h40,8'h80,8'h00,8'h00};
mem_hanzi[ 11 ] = {8'h10,8'h0C,8'h03,8'h00,8'h00,8'h3F,8'h40,8'h40,8'h40,8'h40,8'h40,8'h78,8'h00,8'h01,8'h0E,8'h00};//心1
mem_hanzi[ 12 ] = {8'h00,8'h40,8'h40,8'h48,8'h48,8'hC8,8'h09,8'hFA,8'hC4,8'h00,8'h80,8'h40,8'h20,8'h10,8'h00,8'h00};
mem_hanzi[ 13 ] = {8'h20,8'h20,8'h10,8'h08,8'h06,8'h41,8'h80,8'h7F,8'h00,8'h03,8'h04,8'h08,8'h10,8'h20,8'h20,8'h00};//永2
mem_hanzi[ 14 ] = {8'h00,8'hE0,8'h00,8'hFF,8'h10,8'h20,8'h02,8'hF2,8'h92,8'h92,8'h92,8'h92,8'h92,8'hF2,8'h02,8'h00};
mem_hanzi[ 15 ] = {8'h01,8'h00,8'h00,8'hFF,8'h00,8'h00,8'h40,8'h4F,8'h44,8'h44,8'h44,8'h44,8'h44,8'h4F,8'h40,8'h00};//恒3
end
2'd1: begin
mem_hanzi[ 8 ] = {8'h80,8'h64,8'h2C,8'h34,8'h24,8'h24,8'hEC,8'h32,8'h22,8'h22,8'h32,8'h2E,8'h23,8'hA2,8'h60,8'h00};//
mem_hanzi[ 9 ] = {8'h00,8'h41,8'h21,8'h91,8'h89,8'h87,8'h4D,8'h55,8'h25,8'h25,8'h55,8'h4D,8'h81,8'h80,8'h80,8'h00};//爱0
mem_hanzi[ 10 ] = {8'h00,8'h04,8'h24,8'h24,8'h25,8'h26,8'h24,8'hFC,8'h24,8'h26,8'h25,8'h24,8'h24,8'h04,8'h00,8'h00};//
mem_hanzi[ 11 ] = {8'h81,8'h89,8'h89,8'h49,8'h49,8'h29,8'h19,8'h0F,8'h19,8'h29,8'h49,8'h49,8'h89,8'h89,8'h81,8'h00};//美1
mem_hanzi[ 12 ] = {8'h02,8'h02,8'hF2,8'h12,8'h12,8'h12,8'hF2,8'h02,8'hF2,8'h12,8'h12,8'h12,8'hF2,8'h02,8'h02,8'h00};//
mem_hanzi[ 13 ] = {8'h00,8'h00,8'hFF,8'h01,8'h46,8'h80,8'h7F,8'h00,8'hFF,8'h01,8'h46,8'h80,8'h7F,8'h00,8'h00,8'h00};//丽2
mem_hanzi[ 14 ] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};// 3
mem_hanzi[ 15 ] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};// 4
end
2'd2: begin
mem_hanzi[ 8 ] = {8'h02,8'h02,8'hE2,8'h22,8'h22,8'h32,8'h2A,8'h26,8'h22,8'h22,8'h22,8'h22,8'hE2,8'h02,8'h02,8'h00};
mem_hanzi[ 9 ] = {8'h00,8'h00,8'hFF,8'h42,8'h42,8'h42,8'h42,8'h42,8'h42,8'h42,8'h42,8'h42,8'hFF,8'h00,8'h00,8'h00};//百0
mem_hanzi[ 10 ] = {8'h00,8'h20,8'h18,8'hC7,8'h44,8'h44,8'h44,8'h44,8'hFC,8'h44,8'h44,8'h44,8'h44,8'h04,8'h00,8'h00};
mem_hanzi[ 11 ] = {8'h04,8'h04,8'h04,8'h07,8'h04,8'h04,8'h04,8'h04,8'hFF,8'h04,8'h04,8'h04,8'h04,8'h04,8'h04,8'h00};//年1
mem_hanzi[ 12 ] = {8'h02,8'h02,8'hF2,8'h8A,8'h46,8'h00,8'hFC,8'h04,8'hFC,8'h04,8'h02,8'hFE,8'h03,8'h02,8'h00,8'h00};
mem_hanzi[ 13 ] = {8'h42,8'h82,8'h7F,8'h00,8'h80,8'h60,8'h1F,8'h00,8'h7F,8'h28,8'h10,8'h61,8'h0E,8'h30,8'h40,8'h00};//孤2
mem_hanzi[ 14 ] = {8'h10,8'h0C,8'h04,8'h04,8'hF4,8'h24,8'h25,8'h26,8'h04,8'hE4,8'h24,8'h24,8'h24,8'hF4,8'h0C,8'h00};
mem_hanzi[ 15 ] = {8'h21,8'h11,8'h49,8'h81,8'h7D,8'h01,8'h09,8'h31,8'h80,8'h41,8'h26,8'h18,8'h26,8'h41,8'h80,8'h00};//寂3
end
endcase
//此处可以导入需要的字模
end
/*汉字:硬禾学堂
以汉字“硬禾学堂”RAM生成步骤为例:
1、阴码、列行式、逆向 生成字模
2、然后把生成的字模作为RAM的元素
*/
always@(posedge key_down, negedge rst_n) begin
if (!rst_n) flag = 1'b0;
else if (key_down) begin
if (flag>=2'd2) flag=2'd0;
else flag = flag + 1'b1;
end
else;
end
endmodule
4.3 用含有代码消抖的按键切换歌曲
抖动的产生 :通常的按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。
软件消除抖动的实现方法:检测到按键按下动作之后进行10ms~20ms左右的延时,当前沿的抖动消失之后再一次检测按键的状态。如果仍然是按下的电平状态,则认为这是一次真正的按键按下;同样检测到按键释放,也要做10ms~20ms延时,检测到后沿抖动消失后认为是一个完整的按键弹起过程。
按键按下消抖模块module debounce_down代码如下:
module debounce_down (clk,rst,key,key_down);
parameter N = 1; //要消除的按键的数量
input clk;
input rst;
input [N-1:0] key; //输入的按键
output [N-1:0] key_down; //按键动作产生的脉冲
reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
//利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
always @(posedge clk or negedge rst) begin
if (!rst) begin
key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
end
end
assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器
//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
always @(posedge clk or negedge rst) begin
if(!rst)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量
reg [N-1:0] key_sec;
//延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec <= {N{1'b1}};
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
assign key_down = key_sec_pre & (~key_sec);
endmodule
按键释放消抖模块module debounce_up代码如下:
module debounce_up (clk,rst,key,key_up);
parameter N = 1; //要消除的按键的数量
input clk;
input rst;
input [N-1:0] key; //输入的按键
output [N-1:0] key_up; //按键动作产生的脉冲
reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
//利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
always @(posedge clk or negedge rst) begin
if (!rst) begin
key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
end
end
assign key_edge = key_rst & (~key_rst_pre);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器
//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
always @(posedge clk or negedge rst) begin
if(!rst)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量
reg [N-1:0] key_sec;
//延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec <= {N{1'b1}};
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
assign key_up = key_sec & (~key_sec_pre);
endmodule
4.4 呼吸灯指示按键的触发
核心板上的RGB LED原理图如下图所示(图七)。由此可见,当收到高电平,LED不导通,不发光;当收到低电平,LED导通,发光。
图七: RGB LED原理图
原理:通过PWM脉宽调制决定LED的明暗程度。在按键回弹后的一个时间段T(=1s)内不变,调整高电平脉宽t的时间,从而改变占空比,实现LED由明到暗的效果。
通过两个计数器cnt1和cnt2,cnt1随系统时钟同步计数(系统时钟上升沿时cnt1自加1)范围为0~T,cnt2随cnt1的周期同步计数(cnt1等于T时,cnt2自加1)范围也是0~T,这样每次cnt1在0~T的计数时,cnt2为一个固定值,相邻cnt1计数周期对应的cnt2的值逐渐增大,我们将cnt1计数0~T的时间作为脉冲周期,cnt2的值作为脉冲宽度,则占空比 = cnt2/T,占空比从0%到100%的时间 = cnt2*cnt1 = T^2 = 1s = 12M个系统时钟,T = 2400,我们定义CNT_NUM = 2400作为两个计数器的计数最大值。当cnt1<cnt2时,输出高电平;反之,输出低电平。
模块module press_key代码如下:
module press_key(clk,click,ledr,ledg,ledb);
input clk, click;
output ledr, ledg, ledb;
reg [11:0] cnt1, cnt2;
parameter CNT = 2400;
always@(posedge clk, negedge click) begin
if (!click) cnt1 <= 12'd0;
else if (cnt1 >= CNT-1) cnt1 <= 12'd0;
else cnt1 <= cnt1 + 1'b1;
end
always@(posedge clk, negedge click) begin
if (!click) cnt2 <= 12'd0;
else if (cnt2 < CNT) begin
if (cnt1 >= CNT-1) cnt2<=cnt2+1'b1;
else;
end
else;
end
assign ledr=(cnt1<cnt2)?1'b1:1'b0;
assign ledg=(cnt1<cnt2)?1'b1:1'b0;
assign ledb=(cnt1<cnt2)?1'b1:1'b0;
endmodule
将OLED显示与LED显示合成一个显示的顶层模块module vision_top,代码如下:
module vision_top(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input key_in,
output oled_rst, //OLCD液晶屏复位
output oled_dcn, //OLCD数据指令控制
output oled_clk, //OLCD时钟信号
output oled_dat, //OLCD数据信号
output ledr_out,
output ledg_out,
output ledb_out,
output [1:0] flag,
output key_down
);
OLED12864 OLED12864_u1 (
.clk (clk_in) ,
.rst_n (rst_n_in),
.key_up (key_up),
.key_down (key_down),
//.oled_csn(oled_csn),
.oled_rst(oled_rst),
.oled_dcn(oled_dcn),
.oled_clk(oled_clk),
.oled_dat(oled_dat),
.flag(flag)
);
press_key response(
.clk (clk_in) ,
.click (key_in),
.ledr(ledr_out),
.ledg(ledg_out),
.ledb(ledb_out)
);
debounce_up up(
.clk (clk_in),
.rst (rst_n_in),
.key (key_in),
.key_up (key_up)
);
debounce_down down(
.clk (clk_in),
.rst (rst_n_in),
.key (key_in),
.key_down(key_down)
);
endmodule
项目顶层模块module top例化了声音与显示的顶层模块Auditory_top与vision_top,代码如下:
module top (
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input key_in,
output oled_rst, //OLCD液晶屏复位
output oled_dcn, //OLCD数据指令控制
output oled_clk, //OLCD时钟信号
output oled_dat, //OLCD数据信号
output ledr_out,
output ledg_out,
output ledb_out,
output drive
);
wire key_down;
wire [1:0] flag;
vision_top u1(
.clk_in (clk_in),
.rst_n_in (rst_n_in),
.key_in (key_in),
.oled_rst (oled_rst), //OLCD液晶屏复位
.oled_dcn (oled_dcn), //OLCD数据指令控制
.oled_clk (oled_clk), //OLCD时钟信号
.oled_dat (oled_dat), //OLCD数据信号
.ledr_out (ledr_out),
.ledg_out (ledg_out),
.ledb_out (ledb_out),
.flag (flag),
.key_down (key_down)
);
auditory_top u2(
.clk (clk_in),
.rst (rst_n_in),
.key_down (key_down),
.flag (flag),
.drive (drive)
);
endmodule
5 FPGA资源占用报告
FPGA资源占用报告如图八、九所示。
图八:资源占用(1)
图九:资源占用(2)
6 遇到的主要难题
FPGA驱动SSD1306-OLED, 歌曲对照简谱转化为音符。
7 未来的改进
利用另一个按键,实现歌曲的播放与暂停功能,并在OLED上显示“已暂停”。