2025寒假练 - 基于iCE40UP5K的可定时的音乐时钟
该项目使用了iCE40UP5K,实现了可定时的音乐时钟的设计,它的主要功能为:可定时的音乐时钟,OLED显示时间,按键定时,时间到蜂鸣器响,灯光闪烁,持续5s。
标签
FPGA
ICE40UP5K
2025寒假练
木木木111
更新2025-03-24
北京理工大学
30
基于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,图中为000056

关键代码解析

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  

5OLED显示模块:通过时钟模块输出的时分秒,将时间显示在OLED屏幕上。OLED部分代码主要参考MMA7660OLED驱动模块。

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资源占用报告

附件下载
pro1.zip
这个代码里面能实现彩灯对应小时(为了演示改成对应秒数,只要修改一个变量就可以变成对应小时),通过按钮k1,可以在特定的时间点灯光闪烁+蜂鸣器播放音乐,持续5s
oled_show.zip
这个文件里面的代码能够单独显示时间,可能是资源占用的问题,它无法与其他模块共同显示,因此单独放在此处
oled_show_impl_1.rbt
这个文件烧进去可以让oled显示时间
WS2812_led_impl_1.rbt
这个文件烧进去会按秒亮灯,之后按按键可以定时,到点闪烁加播放,但oled模块是乱码,可以看上面的文件显示时间
团队介绍
木木木111
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号