-
项目2 - 利用PWM制作一个音乐播放器
-
通过PWM产生不同的音调,并驱动板上蜂鸣器将音调输出
-
能够播放三首不同的曲子,每个曲子的时间长度为1分钟,可以切换播放
-
曲子的切换使用扩展板的按键,需要有按键消抖的功能
-
播放的曲子的名字在OLED屏幕上显示出来(汉字显示)
-
这是本项目的要求。本项目的设计框架为下图去
下面介绍各个模块的含义
其中debounce模块是用来实现按键消抖功能,因为该按键为机械按键,在按下时按键内部会出现多次闭合断开的现象,此现象称为按键抖动,所以需要将按键按下后延时一段时间等按键稳定后再读取按键的值。因为本模块使用了播放暂停和切换曲目的功能,故需要将消抖模块例化两次。
Beeper模块,是用PWM来实现不同音调,从而达到播放歌曲的结果。将播放不同歌曲的标志信号传递给OLED。
OLED模块,是将Beepe传递过来的标志信号转化为对应的歌曲名字并显示再OLED上。
下面介绍下板子的硬件电路,
Buzzer为蜂鸣器,通过管脚GPIO19控制,小彩灯通过GPIO18控制。
12个WS2818B小彩灯通过串联的方式连接起来。
由于寒假有部悬疑剧《开端》很火,所以想着自己根据数字乐谱扣个卡农的歌曲出来,为了仿真爆炸效果,选择用小彩灯的闪烁来表示。
最重实现的效果为
所占用的硬件资源为:
可以看出使用了1599个查找表,12个IO Buffers 749个寄存器,这些都是FPGA的基本结构单元
12个彩灯闪烁着不同的颜色,蜂鸣器播放着音乐。
遇到的难点及解决方法、未来计划及建议:
本次项目遇到的难点及解决方法有如下几点:
1、之前没解除过lattice的FPGA芯片,所以不太熟悉Radiant 软件,不过这个问题倒是不大,都是EDA软件,需要用到的基本功能和VIVADO和 quartus的大差不差,最基本的新建工程综合约束管脚定义输出文件的格式更改这些基本操作。
2、本FPGA芯片是以模块的形式插在下面的母板上面的,因此电路的原理分为两张,要结合起来看,这也是个难点,通过40个插针讲两张原理图连接了起来,正确的找到FPGA芯片与具体模块的连接才能添加正确的管脚定义。
3、本项目难度不大,用到的模块不多,其中遇到的问题这里一并说了,按键消抖模块需要确定好要延时的时间,按下按键后延时一定的时间在识别按键的状态。蜂鸣器模块要确定好音乐的音调所占的pwm的频率,以及在写歌曲时要看懂对应歌曲的数字乐谱,这个在百度上可以查到,比如乐谱中数字带上划线的代表着高音,带下划线的代表着低音,存数字的代表着中音,每一小节与下一小节的衔接,每个音乐对应的速度等。OLED模块则要事先将歌曲的中文汉字取模出来,然后再写入OLED中,写的时候注意当屏幕从第一行写到最后一行时要将计数器置数到屏幕的第一行,这样屏幕才能实现刷屏的功能,才能再按键按下时切换屏幕的显示。LED小彩灯模块没什么难点,主要注意的时如果不想产生循环闪烁的功能,则不需要将LED1的值赋给LED2,LED2的值赋给LED3依次类推。注意好复位是低有效还是高有效的很重要。
未来的计划是想继续深入的学习FPGA,提高自己的编程能力,想完成更大的项目,真正的掌握FPGA的开发经验,想学习高速接口PCIE等。
我对本次活动的建议是,增加网页的ctrl+s的保存功能。因为平时写东西都是在world上写,习惯的按ctrl+s,而在这个网站上按ctrl+s,刚写了半天的字全没了,这点让人很难受。希望该网站能增加ctrl+s功能。
希望未来的FPGA活动可以分难度等级,选择一些稍微中端一点的芯片,出一些难度高点的项目,这样能更深入的锻炼到自己的能力。
主要的代码分为OLED的点亮,汉字数字的刷新,在实现汉字刷屏的时候要注意,当OLED用空格写完一次屏幕后,要跳转到汉字刷新的状态里去,这样当按键按下时,汉字才会在OLED上刷新出不同的歌曲名字。
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'd13) cnt_main <= 5'd9;//接下来执行空操作,实现数据只刷新一次
else cnt_main <= cnt_main + 1'b1;
case(cnt_main) //MAIN状态
5'd0 : begin state <= INIT; end
5'd1 : begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd2 : begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3 : begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd4 : begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd5 : begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd6 : begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd7 : begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd8 : begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd9 : begin y_p <= 8'hb2; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd0; state <= CHINESE; end
5'd10: begin y_p <= 8'hb2; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd2; state <= CHINESE; end
5'd11 : begin y_p <= 8'hb2; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd4; state <= CHINESE; end
5'd12: begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd6; state <= CHINESE; end
5'd13 : begin y_p <= 8'hb2; x_ph <= 8'h16; x_pl <= 8'h00; mem_hanzi_num <= 8'd8; state <= CHINESE; end
// 5'd14: begin y_p <= 8'hb5; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd6; char <= "World!";state <= SCAN; 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
WS2818B彩灯闪烁的代码
//灯圈旋转
always@(posedge clk or negedge rst ) begin
if(!rst)begin
cnt_time<=24'b0;
ledcolor1<=LED_1;
ledcolor2<=LED_2;
ledcolor3<=LED_3;
ledcolor4<=LED_4;
ledcolor5<=LED_5;
ledcolor6<=LED_6;
end
else if(cnt_time == cnt_time_max - 1'b1)begin
cnt_time<=24'b0;
ledcolor1<=ledcolor6;
ledcolor2<=ledcolor1;
ledcolor3<=ledcolor2;
ledcolor4<=ledcolor3;
ledcolor5<=ledcolor4;
ledcolor6<=ledcolor5;
end
else
cnt_time<=cnt_time+1'b1;
end
reg[9:0] cnt_1;
reg flag1;
always@(posedge clk or negedge rst ) begin
if(!rst)begin
cnt_1<=0;
end
else if(cnt_1==1022)begin
cnt_1<=0;
end
else
cnt_1<=cnt_1+1;
end
always@(posedge clk or negedge rst ) begin
if(!rst)begin
flag1<=0;
end
else if(cnt_1==1020)begin
flag1<=~flag1;
end
else
flag1<=flag1;
end
//LEd数据高低周期,一个0/1码持续时间
always @(posedge clk or negedge rst)
begin
if(!rst)
cycle_cnt <= 7'd0;
else if(cycle_cnt == (T0H + T0L - 6'd1))
cycle_cnt <= 7'd0;
else if(state != RST_FSM)//一轮传输下来,复位后对齐
cycle_cnt <= cycle_cnt + 1'b1;
else
cycle_cnt <= 7'd0;
end
//FSM 1
always @ (posedge clk or negedge rst)
begin
if(!rst)
state <= IDLE;
else
state <= state_n;
end
//FSM 2 状态输出
always @ (posedge clk or negedge rst)
begin
if(!rst)
begin
led_pwm <= 1'b0;
shift <= 1'b0;
end
else
begin
case(state)
IDLE :
begin
led_pwm <= 1'b1;//1
end
LED_one:
begin
shift <= ledcolor1[bit_cnt];//随着cnt的计数,变换0/1,
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_two:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_thr:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_fou:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_fiv:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_six:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_sev:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_eig:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_nin:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_ten:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_ele:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
LED_twe:
begin
shift <= ledcolor1[bit_cnt];
if(shift == 1'b1)
begin
if(cycle_cnt == T1H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
else
begin
if(cycle_cnt == T0H)
led_pwm <= 1'b0;
else if(cycle_cnt == (T1H + T0H - 6'd1))
led_pwm <= 1'b1;
else
led_pwm <= led_pwm;
end
end
RST_FSM:
begin
led_pwm <= 1'b0;
end
default:
begin
led_pwm <= 1'b0;
end
endcase
end
end
assign led = led_pwm;
//FSM 3 状态转移
always @ (*)
begin
case (state)
IDLE:
state_n = LED_one;
LED_one:
begin
if(state_tran)
state_n = LED_two;
else
state_n = state;
end
LED_two:
begin
if(state_tran)
state_n = LED_thr;
else
state_n = state;
end
LED_thr:
begin
if(state_tran)
state_n = LED_fou;
else
state_n = state;
end
LED_fou:
begin
if(state_tran)
state_n = LED_fiv;
else
state_n = state;
end
LED_fiv:
begin
if(state_tran)
state_n = LED_six;
else
state_n = state;
end
LED_six:
begin
if(state_tran)
state_n = LED_sev;
else
state_n = state;
end
LED_sev:
begin
if(state_tran)
state_n = LED_eig;
else
state_n = state;
end
LED_eig:
begin
if(state_tran)
state_n = LED_nin;
else
state_n = state;
end
LED_nin:
begin
if(state_tran)
state_n = LED_ten;
else
state_n = state;
end
LED_ten:
begin
if(state_tran)
state_n = LED_ele;
else
state_n = state;
end
LED_ele:
begin
if(state_tran)
state_n = LED_twe;
else
state_n = state;
end
LED_twe:
begin
if(state_tran)
state_n = RST_FSM;
else
state_n = state;
end
RST_FSM:
begin
if(state_tran_rst)
state_n = IDLE;
else
state_n = state;
end
default:
state_n =RST_FSM;
endcase
end
//记录24位的时间,方便转移状态led
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
bit_cnt <= 5'd0;
state_tran <= 1'b0;
end
else if (bit_cnt == 5'd24)
begin
bit_cnt <= 5'd0;
state_tran <= 1'b1;
end
else if(cycle_cnt == (T0H + T0L - 6'd1))
begin
bit_cnt <= bit_cnt + 1'b1;
state_tran <= 1'b0;
end
else
begin
bit_cnt <= bit_cnt;
state_tran <= 1'b0;
end
end
//记录rst的时间,状态转移
always @(posedge clk or negedge rst)
begin
if(!rst)
rst_cnt <= 14'd0;
else if(state == RST_FSM)
rst_cnt <= rst_cnt + 1'b1;
else
rst_cnt <= 14'd0;
end
//rst的时间一到就发状态转移信号,开始下一轮
always @(posedge clk or negedge rst)
begin
if(!rst)
state_tran_rst <= 1'b0;
else if(rst_cnt == RST)
state_tran_rst <=1'b1;
else
state_tran_rst <= 1'b0;
end
蜂鸣器不同音调的实现代码:
always@(tone) begin
case(tone)
5'd1: time_end = 16'd22935; //L1,
5'd2: time_end = 16'd20428; //L2,
5'd3: time_end = 16'd18203; //L3,
5'd4: time_end = 16'd17181; //L4,
5'd5: time_end = 16'd15305; //L5,
5'd6: time_end = 16'd13635; //L6,
5'd7: time_end = 16'd12147; //L7,
5'd8: time_end = 16'd11464; //M1,
5'd9: time_end = 16'd10215; //M2,
5'd10: time_end = 16'd9100; //M3,
5'd11: time_end = 16'd8589; //M4,
5'd12: time_end = 16'd7652; //M5,
5'd13: time_end = 16'd6817; //M6,
5'd14: time_end = 16'd6073; //M7,
5'd15: time_end = 16'd5740; //H1,
5'd16: time_end = 16'd5107; //H2,
5'd17: time_end = 16'd4549; //H3,
5'd18: time_end = 16'd4294; //H4,
5'd19: time_end = 16'd3825; //H5,
5'd20: time_end = 16'd3408; //H6,
5'd21: time_end = 16'd3036; //H7,
default:time_end = 16'd65535;
endcase
end
always@(tcon or flag) begin
if(flag == 3'd1) begin
case(tcon)//卡农,一小节四排,拍速为160拍/分钟
5'd 1: time_delay = 28'd1000000;//四分之一拍
5'd 2: time_delay = 28'd2000000;//半拍
5'd 3: time_delay = 28'd3000000;//四分之三拍
5'd 4: time_delay = 28'd4000000;//一拍
5'd 6: time_delay = 28'd6750000;//一拍半
5'd 8: time_delay = 28'd9000000;//两拍
5'd12: time_delay = 28'd1200000;//三拍
5'd16: time_delay = 28'd18000000;//四拍
5'd24: time_delay = 28'd24000000;//八拍
default:time_delay = 28'd0;
endcase
end
else if(flag == 3'd2) begin
case(tcon)//相思,一小节三拍,拍速为100拍/分钟
5'd 1: time_delay = 28'd1800000;//四分之一拍
5'd 2: time_delay = 28'd3600000;//半拍
5'd 3: time_delay = 28'd5400000;//四分之三拍
5'd 4: time_delay = 28'd7200000;//一拍
5'd 6: time_delay = 28'd10800000;//一拍半
5'd 8: time_delay = 28'd14400000;//两拍
5'd12: time_delay = 28'd21600000;//三拍
5'd16: time_delay = 28'd28800000;//四拍
5'd24: time_delay = 28'd57600000;//八拍
default:time_delay = 28'd0;
endcase
end
else if(flag == 3'd3) begin
case(tcon)//大鱼,一小节四拍,拍速为80拍/分钟
5'd 1: time_delay = 28'd2250000;//四分之一拍
5'd 2: time_delay = 28'd4500000;//半拍
5'd 3: time_delay = 28'd6750000;//四分之三拍
5'd 4: time_delay = 28'd9000000;//一拍
5'd 6: time_delay = 28'd13500000;//一拍半
5'd 8: time_delay = 28'd18000000;//两拍
5'd12: time_delay = 28'd27000000;//三拍
5'd16: time_delay = 28'd36000000;//四拍
5'd24: time_delay = 28'd72000000;//八拍
default:time_delay = 28'd0;
endcase
end
该项目的工程及完整的源代码见百度网盘链接:
链接:https://pan.baidu.com/s/1i1oDMtC60b8kk7AfTCSDaQ
提取码:3lzk
--来自百度网盘超级会员V6的分享