基于FPGA的可定时的音乐时钟设计总结
在这个项目中,我设计了一个基于FPGA的交互式时钟系统,利用iCE40核心板和扩展板上的12颗彩灯来对应12个小时,并通过OLED显示屏实时显示当前的时间(小时、分钟、秒)。此外,系统还具备定时功能,我们可以通过按键设置时间,按下按键开始5s倒计时,每按下一次按键延长一秒倒计时,当达到设定时间时,系统将通过彩灯闪烁和蜂鸣器发声持续5s来提醒。
硬件介绍
本项目使用的硬件包括:
1. iCE40核心板:作为FPGA开发的核心,负责整个系统的逻辑控制和时钟生成。FPGA的灵活性使得我们能够根据需求快速实现各种功能。
2.扩展底板:底板上集成了12颗可寻址的RGB LED灯,这些灯用于显示当前小时的颜色,并在设定时间到达时进行闪烁。
3. OLED显示屏:用于实时显示当前的时间,提供清晰的数字输出,便于我们查看。
4.蜂鸣器:用于音频提醒,当达到设定时间时,蜂鸣器将发出声音,增强提醒效果。
5.按键:用于我们输入,设置时间和启动定时功能。
系统架构
系统的整体架构如下所示:
1.时钟生成:使用12MHz的时钟信号,通过分频实现1Hz的时钟输出,确保时间的准确性。
2.交互控制:采用状态机管理系统的工作流程,确保各个模块之间的协调工作。
3.多模块解耦:各功能模块独立实现,便于后期的维护和升级。
4.灵活配置:通过按键输入实现系统状态的切换和时间的设置,增强我们的交互体验。
软件流程图
实际使用的流程,可能由于资源占用等问题,多次尝试后仍无法将oled显示和彩灯同时进行,所以将功能分开在2个代码中实现
各模块在编译时可行,但在板子中可能由于我的设计缺陷,导致不能同时运行,所以将两个模块分开展示。
实物展示部分
led灯亮起对应小时(秒钟),为了演示用秒钟代替了小时
oled显示时H,分M,秒S,图中为00:00:56
关键代码解析
1.时钟生成模块:该模块负责生成1Hz的时钟信号,并管理秒、分、小时的进位逻辑。通过计数器实现时间的累加,确保时间的准确性。
时钟参数:使用12MHz的时钟信号,通过分频实现1Hz的输出。
进位逻辑:当秒数达到59时,重置为0并增加分钟;当分钟达到59时,重置为0并增加小时;小时在12小时制下循环。
展示时钟模块关键代码:
always @(posedge clk_1s or negedge rst_n) begin
if (!rst_n) begin
seconds <= 8'b0;
minutes <= 8'b0;
hours <= 8'd0;
end else begin
//秒、分、时进位逻辑
if (seconds == 8'd59) begin
seconds <= 8'b0;
if (minutes == 8'd59) begin
minutes <= 8'b0;
if (hours == 8'd12) begin
hours <= 8'd1;
end else if (hours == 8'd11) begin
hours <= hours + 1'b1;
end else begin
hours <= hours + 1'b1;
end
end else begin
minutes <= minutes + 1'b1;
end
end else begin
seconds <= seconds + 1'b1;
end
end
end
2. LED控制模块:该模块根据当前小时选择对应的LED颜色。通过PWM编码实现对WS2812 LED的控制,确保颜色的准确显示。
状态机:通过状态机控制LED的切换,确保在每个小时切换时,LED能够正确显示对应的颜色。
初始设置
parameter LED_1 = 25'b00000_0000_0000_0000_0001_0000;//绿
parameter LED_2 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_3 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_4 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_5 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_6 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_7 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_8 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_9 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_10 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_11 = 25'b00000_0000_0000_0000_0000_0000;
parameter LED_12 = 25'b00000_0000_0000_0000_0000_0000;
转换设置
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;
ledcolor7<=LED_7;
ledcolor8<=LED_8;
ledcolor9<=LED_9;
ledcolor10<=LED_10;
ledcolor11<=LED_11;
ledcolor12<=LED_12;
end
else if(cnt_time == cnt_time_max - 1'b1)begin
cnt_time<=24'b0;
ledcolor1<=ledcolor12;
ledcolor2<=ledcolor1;
ledcolor3<=ledcolor2;
ledcolor4<=ledcolor3;
ledcolor5<=ledcolor4;
ledcolor6<=ledcolor5;
ledcolor7<=ledcolor6;
ledcolor8<=ledcolor7;
ledcolor9<=ledcolor8;
ledcolor10<=ledcolor9;
ledcolor11<=ledcolor10;
ledcolor12<=ledcolor11;
end
else
cnt_time<=cnt_time+1'b1;
end
彩灯对应时间,以1s为演示,只要更改就可以实现1小时亮1个对应的灯
parameter cnt_time_max = 1_2000_000;
//parameter cnt_time_max = 43200000000;
3.蜂鸣器控制模块:该模块负责控制蜂鸣器的发声,根据设定的音调和频率生成声音。
音调生成:通过计数器控制蜂鸣器的频率,实现不同的音调效果。
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
4.主控制模块:该模块管理系统的整体工作流程,处理按键输入,控制LED和蜂鸣器的工作状态。
状态机设计:通过状态机管理系统的不同状态,如IDLE、设置时间、启动定时等,确保系统的正常运行。
通过按键设置定时时间,每按下一次相当于设置了多1s的时间,当达到设定时间时,LED灯闪烁并伴随蜂鸣器发声,持续5秒钟
ACTIVE: begin
//检测到按键的上升沿,增加1秒延时
if (key_in && !key_in_prev) begin
delay_counter <= delay_counter + ADD_TIME;
end
// LED和蜂鸣器同时工作
if (delay_counter < ACTIVE_TIME) begin
delay_counter <= delay_counter + 1'b1;
wait_active <= 1'b1; //保持wait_active为高
tone_en <= 1'b1;
end
else begin
current_state <= RESET;
delay_counter <= 26'd0;
wait_active <= 1'b0; //关闭wait_active
tone_en <= 1'b0;
end
end
5、OLED显示模块:通过时钟模块输出的时分秒,将时间显示在OLED屏幕上。OLED部分代码主要参考MMA7660的OLED驱动模块。
5'd1: begin y_p <= 8'hb0; x_ph <= 8'h3 ; x_pl <= 8'h00; num <= 5'd16; char <= "S: "; state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h3 ; x_pl <= 8'h00; num <= 5'd16; char <= "M: "; state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h3 ; x_pl <= 8'h00; num <= 5'd16; char <= "H: "; 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'hb0; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd 1; char <= x_data_bcd%10; state <= SCAN; end
5'd10: begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd 1; char <= x_data_bcd/10; state <= SCAN; end
5'd11: begin y_p <= 8'hb1; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd 1; char <= y_data_bcd%10; state <= SCAN; end
5'd12: begin y_p <= 8'hb1; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd 1; char <= y_data_bcd/10; state <= SCAN; end
5'd13: begin y_p <= 8'hb2; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd 1; char <= z_data_bcd%10; state <= SCAN; end
5'd14: begin y_p <= 8'hb2; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd 1; char <= z_data_bcd/10; state <= SCAN; end
OLED屏实时显示当前的小时、分钟和秒,我们可以清晰地看到时间的变化。
LED灯光效果
项目中遇到的难题
难题
1.精确时序控制:在FPGA中实现精确的时序控制是一个挑战,尤其是在处理多个模块时。
2.多模块同步:确保各个模块之间的同步工作,避免出现时间延迟或错误。由于OLED程序设计可能存在缺陷,当彩灯模块和蜂鸣器模块接入时,会出现乱码情况,因此目前的项目只能单独呈现程序部分模块的功能,但所有的功能都是齐全的,只是在资源利用可能存在不足。
3.低功耗设计:在设计过程中,需要考虑功耗问题,确保系统在运行时不会过热。
对本次活动的心得体会
通过本项目,我深入理解了FPGA的应用和数字系统的设计方法。项目的实施让我认识到模块化设计的重要性,良好的模块化设计不仅提高了代码的可读性,还便于后期的维护和扩展。由于之前没有相关的硬件设计经验,所以在完成项目时遇到了很多问题,但这次的项目也让我收获良多,我之后需要设计更加流畅和完整的功能。
FPGA资源占用报告: