基于iCE40UP5K的FPGA学习平台利用PWM制作的音乐播放器
在超低功耗的ICE FPGA开发板上利用PWM波实现了音乐播放的效果,同时WS2812彩灯在不停的闪烁
标签
FPGA
寒假在家一起练
爆肝儿
更新2022-03-03
中山大学
740
  1. 项目2 - 利用PWM制作一个音乐播放器
    1. 通过PWM产生不同的音调,并驱动板上蜂鸣器将音调输出
    2. 能够播放三首不同的曲子,每个曲子的时间长度为1分钟,可以切换播放
    3. 曲子的切换使用扩展板的按键,需要有按键消抖的功能
    4. 播放的曲子的名字在OLED屏幕上显示出来(汉字显示)

这是本项目的要求。本项目的设计框架为下图去

 

FhQx4IeDOWdRAu0KRRu-NxgMpGA7

下面介绍各个模块的含义

其中debounce模块是用来实现按键消抖功能,因为该按键为机械按键,在按下时按键内部会出现多次闭合断开的现象,此现象称为按键抖动,所以需要将按键按下后延时一段时间等按键稳定后再读取按键的值。因为本模块使用了播放暂停和切换曲目的功能,故需要将消抖模块例化两次。

 

Beeper模块,是用PWM来实现不同音调,从而达到播放歌曲的结果。将播放不同歌曲的标志信号传递给OLED。

 

OLED模块,是将Beepe传递过来的标志信号转化为对应的歌曲名字并显示再OLED上。

 

 

下面介绍下板子的硬件电路,

FvDyCny0-s5Q1ymyzzgtEb3tqIXm

 

 

Buzzer为蜂鸣器,通过管脚GPIO19控制,小彩灯通过GPIO18控制。

 

FvwPrPhoeDnpexcvgXK2cJXam7Xh

12个WS2818B小彩灯通过串联的方式连接起来。

由于寒假有部悬疑剧《开端》很火,所以想着自己根据数字乐谱扣个卡农的歌曲出来,为了仿真爆炸效果,选择用小彩灯的闪烁来表示。

 

最重实现的效果为

 

Fumb3V0HtN_BfiZGR8A7rcL0SWYu

 

所占用的硬件资源为:

FsrPk6uuuQx3tHFYSJWIcjRVlYuc

 

可以看出使用了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的分享

 

团队介绍
个人
团队成员
爆肝儿
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号