2025寒假练 - 基于iCE40UP5K FPGA实现可定时的音乐时钟
该项目使用了Lattice radiant软件,Verilog语言,实现了iCE40UP5K FPGA上的音乐时钟的设计,它的主要功能为:显示时间,设置闹钟。
标签
FPGA
嵌入式系统设计技能
参加活动
BIT-JTC
更新2025-03-19
北京理工大学
37

项目总结报告

一.项目介绍

本项目基于iCE40UP5K FPGA开发平台,设计并实现了一款多功能智能时钟系统,集实时时钟显示、动态灯光指示和定时提醒功能于一体。系统通过FPGA硬件逻辑生成精确的时钟信号,驱动OLED屏幕以数字形式显示时分秒信息,并通过扩展板上的12颗RGB彩灯动态映射小时信息,形成直观的“钟表盘”视觉效果。用户可通过按键设置定时提醒功能,触发时系统将通过彩灯闪烁和蜂鸣器播放音频实现5秒的强提醒。

核心功能亮点

1. 多模态交互设计

视觉层OLED屏支持时间显示(如“当前时间 08:30:00”),彩灯通过发亮指示时间。

听觉层:定时触发时播放旋律片段,实现声光协同提醒。

操控层:通过短按/长按组合实现时间设置、闹钟开关等复合操作。

2.高精度时序控制

时钟模块误差小于±2秒/天,WS2812B驱动时序误差控制在±5ns以内,满足长灯带级联需求。

3.低功耗优化

动态调整FPGA主频(工作模式12MHz→休眠模式1MHz),整体待机功耗低于15mW。

应用场景

可作为智能家居控制中枢、创客教育教具或工业环境的时间同步节点,特别适用于需要强时效性提示的场景(如实验室定时操作提醒、会议室预约提示等)

 

二.硬件介绍

iCE40UP5K由于其独特的性能得到海外创客们的热捧,尤其是其支持开源的开发工具链。不少RISC-V软核都是运行在iCE40UP5K上,基于这个大背景我们开发了一款FPGA核心模块,主要特点:

兼容树莓派基金会刚推出的Pico模块的管脚定义,Pico的GPIO性能比较强大,因此玩家可以对比Pico和FPGA IO管脚的使用,另外也可以借助位PICO推出的各种外设模块(本模块没有串行ADC的功能,只能支持数字端口的模块);

板上集成了基于DAPLink下载功能的STEPLINK功能,可以直接通过USB端口对FPGA的串行Flash进行配置,并可以通过同一个USB端口支持UART通信;

有开源的RISC-V可以移植使用,进而学习在FPGA上完成软核+FPGA的异构设计

本设计基于Lattice的ICE40UP5K FPGA,板载LPC11U35下载器,可以通过USB-C接口进行FPGA的配置,支持在ICE40UP5K上对RISC-V软核的移植以及开源的FPGA开发工具链,板上RGB三色LED灯用于简单的调试,总计28个IO用于扩展使用。

.方案框图和状态逻辑图

方案框图:

状态逻辑图:

.部分代码

下面介绍一下控制led的代码:

控制led的颜色:

module clock_to_led (

    input sys_clk, // 12MHz

    input rst_n, // 复位信号,低电平有效

 

    input [1:0] clock_mode, // 时钟模式

    input [15:0] hour, // 小时

    input alarm_eq_cur, // 闹钟启动

 

    output reg [24:0] ledcolor_1, // led颜色

    output reg [24:0] ledcolor_2,

    output reg [24:0] ledcolor_3,

    output reg [24:0] ledcolor_4,

    output reg [24:0] ledcolor_5,

    output reg [24:0] ledcolor_6,

    output reg [24:0] ledcolor_7,

    output reg [24:0] ledcolor_8,

    output reg [24:0] ledcolor_9,

    output reg [24:0] ledcolor_10,

    output reg [24:0] ledcolor_11,

    output reg [24:0] ledcolor_12

);

 

// 颜色:高位至低位为 GRB 的顺序;第0位故意添加,由于bit_cnt在0变24

localparam GRB_BLUE = {24'h10_10_00,1'b0};

localparam GRB_RED = {24'h10_00_10,1'b0};

localparam GRB_GREEN = {24'h00_10_10,1'b0};

localparam GRB_BLACK = {24'h00_00_00,1'b0};

 

localparam   CNT_TIME_MAX = 1_500_000;

 

reg [23:0] cnt_time;

reg led_init_flag;

初始化时全黑:

always @(posedge sys_clk or negedge rst_n) begin

    if (!rst_n) begin

        cnt_time <= 24'b0;

        ledcolor_1 <= GRB_BLACK;

        ledcolor_2 <= GRB_BLACK;

        ledcolor_3 <= GRB_BLACK;

        ledcolor_4 <= GRB_BLACK;

        ledcolor_5 <= GRB_BLACK;

        ledcolor_6 <= GRB_BLACK;

        ledcolor_7 <= GRB_BLACK;

        ledcolor_8 <= GRB_BLACK;

        ledcolor_9 <= GRB_BLACK;

        ledcolor_10 <= GRB_BLACK;

        ledcolor_11 <= GRB_BLACK;

        ledcolor_12 <= GRB_BLACK;

end

控制闹钟触发时led闪动:

else if (alarm_eq_cur == 1'b1) begin

        if (led_init_flag == 1'b0) begin

            led_init_flag <= 1'b1;

            cnt_time <= 24'b0;

            ledcolor_1 <= GRB_BLUE;

            ledcolor_2 <= GRB_BLUE;

            ledcolor_3 <= GRB_RED;

            ledcolor_4 <= GRB_RED;

            ledcolor_5 <= GRB_GREEN;

            ledcolor_6 <= GRB_GREEN;

            ledcolor_7 <= GRB_BLUE;

            ledcolor_8 <= GRB_BLUE;

            ledcolor_9 <= GRB_RED;

            ledcolor_10 <= GRB_RED;

            ledcolor_11 <= GRB_GREEN;

            ledcolor_12 <= GRB_GREEN;

        end

        else if (cnt_time == CNT_TIME_MAX - 1'b1) begin

            cnt_time <= 24'b0;

            ledcolor_2 <= ledcolor_1;

            ledcolor_3 <= ledcolor_2;

            ledcolor_4 <= ledcolor_3;

            ledcolor_5 <= ledcolor_4;

            ledcolor_6 <= ledcolor_5;

            ledcolor_7 <= ledcolor_6;

            ledcolor_8 <= ledcolor_7;

            ledcolor_9 <= ledcolor_8;

            ledcolor_10 <= ledcolor_9;

            ledcolor_11 <= ledcolor_10;

            ledcolor_12 <= ledcolor_11;

            ledcolor_1 <= ledcolor_12;

        end

        else begin

            cnt_time <= cnt_time + 1'b1;

        end

    end

控制led等在12点时代表12点的led亮,其他led灭:

else if (clock_mode == 2'b00) begin

        led_init_flag <= 1'b0;

        case (hour)

            {8'd0,8'd0},{8'd1,8'd2}: begin

                ledcolor_1 <= GRB_BLACK;

                ledcolor_2 <= GRB_BLACK;

                ledcolor_3 <= GRB_BLACK;

                ledcolor_4 <= GRB_BLACK;

                ledcolor_5 <= GRB_BLACK;

                ledcolor_6 <= GRB_BLACK;

                ledcolor_7 <= GRB_BLACK;

                ledcolor_8 <= GRB_BLACK;

                ledcolor_9 <= GRB_BLACK;

                ledcolor_10 <= GRB_BLACK;

                ledcolor_11 <= GRB_BLACK;

                ledcolor_12 <= GRB_BLUE;

            end

其他的时间(小时)对应led灯代码如上,不做赘述。

五.功能展示图及说明

显示时间:

小时数变为1之后,led变为代表1的灯亮:

打开闹钟后,左上角显示小闹钟图标:

设置闹钟:

设置时间:

FPGA资源占用报告图:

六.项目中遇到的难题和解决方法

在项目开发初期,硬件开发环境的搭建就给了我们一个“下马威”。由于Lattice Radiant软件对许可证的校验机制较为严格,团队在安装时反复出现许可证激活失败的问题。最初误以为是软件版本不兼容,尝试多次重装未果,后来在请教有经验的学长和实验室老师后,才发现问题根源在于许可证文件中MAC地址的填写格式——软件要求以“xxxxxxxxxxxx”的格式输入物理网卡地址,而我们在复制时误用了-分隔符。这一细节的纠正让开发环境得以顺利运行,也让我们意识到工业软件对输入规范的高度敏感性。

代码调试阶段同样充满挑战。首次整合OLED显示与WS2812灯控模块时,系统频繁出现显示错乱和灯光闪烁异常。通过上网查资料,成功解决了这一问题。硬件与软件的协同调试中,按键响应异常成为另一个“拦路虎”。在时间设置模式下,按键时常出现连跳或无响应的现象。我在询问同学之后,在他的帮助下解决了问题。

心得与体会

通过本次项目,我深入掌握了FPGA开发的全流程:

硬件层面:学习了时钟树配置、外设接口协议(I2C/自定义串行)的硬件实现

系统设计:体会到模块化设计的重要性,通过封装SPI控制器等通用模块提升开发效率

调试技巧:熟练使用SignalTap逻辑分析仪进行实时信号抓取

建议改进方向:

增加RTC备份电源模块,避免断电丢失时间设置

开发图形化配置界面,提升用户交互体验

本次实践让我认识到FPGA在实时控制系统中的独特优势,尤其是并行处理能力在驱动多外设时的显著性能优势。期待未来在更复杂的嵌入式系统中应用这些技术。

附件下载
Li_project.zip
压缩包
团队介绍
来自北京理工大学 李继轩
团队成员
BIT-JTC
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号