项目介绍
可定时的音乐时钟
1、使用扩展板上的12颗彩灯对应于12个小时
2、核心板上的FPGA产生时钟,在OLED显示评上通过模拟或者数字的方式显示当前的时间 - 小时、分、秒
3、将“小时”的信息通过12颗彩灯来显示,效果自行设计
4、具有定时的功能,通过扩展板上的按键设置时间,到该时间点即(彩灯闪烁+ 音频播放),持续5秒钟时间
5、音频播放通过扩展板上的蜂鸣器来实现
使用板卡:iCE40核心板+一圈灯底板
硬件介绍
iCE40UP5K 由于其独特的性能得到海外创客们的热捧,尤其是其支持开源的开发工具
链。主要特点:
1、兼容树莓派基金会刚推出的 Pico 模块的管脚定义,Pico 的 GPIO 性能比较强大,因此玩家可以对比 Pico 和 FPGA IO 管脚的使用,另外也可以借助位 PICO 推出的各种外设模块 (本模块没有串行 ADC 的功能,只能支持数字端口的模块);
2、板上集成了基于 DAPLink 下载功能的 STEPLINK 功能,可以直接通过 USB 端口对FPGA 的串行 Flash 进行配置,并可以通过同一个 USB 端口支持 UART 通信;
3、有开源的 RISC-V 可以移植使用,进而学习在 FPGA 上完成软核+FPGA 的异构设计本设计基于 Lattice 的 ICE40UP5K FPGA,板载 LPC11U35 下载器,可以通过 USB-C 接口进行 FPGA 的配置,支持在 ICE40UP5K 上对 RISC-V 软核的移植以及开源的 FPGA 开发工 具链,板上 RGB 三色 LED 灯用于简单的调试,总计 28 个 IO 用于扩展使用。
板载资源:
1 路 USB C 接口;
1 个 RGB 三色 LED;
1 个 12MHz 晶振时钟;
28 个用户可扩展 I/O;
集成USB-Blaster 编程器。
方案
1、核心模块:
FPGA (iCE40UP5K):负责时钟生成、时间显示、彩灯控制、定时功能、音频播放等。
RISC-V软核:用于处理复杂的逻辑和控制任务,如时间计算、定时器管理、按键处理等。
2、外围模块:
OLED显示屏:显示当前时间(小时、分钟、秒)。
12颗彩灯:用于显示小时信息。
按键:用于设置时间。
蜂鸣器:用于定时提醒时的音频播放。
3、通信接口:
UART:用于调试和通信。
DAPLink:用于程序下载和调试。
项目设计思路
1、时钟生成:
使用FPGA内部的时钟分频器生成1Hz的时钟信号,用于计时。
通过计数器实现秒、分、时的累加。
2、时间显示:
使用OLED显示屏显示当前时间,小时、分钟、秒。
使用RISC-V软核处理时间数据的更新和显示。
3、彩灯控制:
12颗彩灯对应于12个小时,当前小时对应的彩灯亮起,其他彩灯熄灭
4、定时功能:
通过按键设置定时时间,FPGA内部实现定时器功能。
到达定时时间后,彩灯闪烁,蜂鸣器播放音频,持续5秒钟。
5、音频播放:
使用蜂鸣器播放简单的音频信号,通过PWM控制蜂鸣器的频率和持续时间。
功能框图
软件流程图
关键代码介绍
屏幕显示:
module time_display(
input wire clk_1hz, // 1Hz时钟信号
input wire reset, // 复位信号
output reg [5:0] hour, // 小时
output reg [5:0] minute,// 分钟
output reg [5:0] second // 秒
);
always @(posedge clk_1hz or posedge reset) begin
if (reset) begin
hour <= 0;
minute <= 0;
second <= 0;
end else begin
if (second == 59) begin
second <= 0;
if (minute == 59) begin
minute <= 0;
if (hour == 23) begin
hour <= 0;
end else begin
hour <= hour + 1;
end
end else begin
minute <= minute + 1;
end
end else begin
second <= second + 1;
end
end
end
endmodule
always @(posedge clk or negedge rst)
begin
if(!rst) begin
// 复位逻辑
end else begin
case(state)
IDLE: begin
// 空闲状态逻辑
end
MAIN: begin
// 主状态逻辑
case(oled_mode)
4'd0: begin
// 模式0的显示逻辑
end
// 其他模式的处理逻辑
endcase
end
按下K1键,切换模式:
always @(posedge clk or negedge rst)begin
if (!rst)
oled_mode <= 4'd0;
else if (key1_pulse)
if(oled_mode == OLED_MODE_MAX - 1'b1)
oled_mode <= 1'b0;
else
oled_mode <= oled_mode + 1'b1;
else
oled_mode <= oled_mode;
end
按下K2键,设置时间:
always @(posedge clk or negedge rst)begin
if (!rst)
key2_mode7 <= 4'd0;
else if (key2_pulse && oled_mode == 7)
if(key2_mode7 == KEY2_MODE7_MAX - 1'b1)
key2_mode7 <= 1'b0;
else
key2_mode7 <= key2_mode7 + 1'b1;
else
key2_mode7 <= key2_mode7;
end
彩灯控制:
module led_control(
input wire clk_1hz,
input wire reset,
input wire [5:0] hour,
output reg [11:0] leds
);
always @(posedge clk_1hz or posedge reset) begin
if (reset) begin
leds <= 12'b000000000000;
end else begin
leds <= 12'b000000000000; // 默认所有灯熄灭
leds[hour % 12] <= 1; // 当前小时对应的灯亮起
end
end
endmodule
蜂鸣器启动
always@(posedge clk or negedge rst)
if(!rst)
beeper_start <= 1'b0;
else if(oled_mode == 0 && diff_hour == 0 && diff_min == 0 && diff_sec == 0)
beeper_start <= 1'b1;
else
beeper_start <= 1'b0;
FPGA资源报告:
功能展示
上方为当前时间,下方为闹钟时间,12颗彩灯对应于12个小时,当前小时对应的彩灯亮起,其他彩灯熄灭
到达闹钟时间时,彩灯闪烁,音频播放
点击k1和k2键调试时钟的时间显示
技术难点与解决方案
1、彩灯同步控制:12颗彩灯需要同步控制,且需要根据当前小时动态点亮对应的彩灯。
解决方案:使用并行控制逻辑,通过一个12位的寄存器直接控制所有彩灯的状态,确保同步性和实时性。
2、定时功能的实现:定时功能需要精确到秒,且需要在到达定时时间时触发多个事件(彩灯闪烁、蜂鸣器播放)。
解决方案:使用一个独立的定时器模块,实时比较当前时间与设定时间,并在匹配时触发事件,通过状态机控制事件的持续时间和顺序。
3、FPGA调试过程中,遇到问题难以定位,某个模块工作不正常,但无法快速找到原因。
解决办法:使用仿真工具(如ModelSim)对各个模块进行仿真测试,确保每个模块的功能正确,同时,在硬件调试时,通过LED指示灯或串口打印调试信息,帮助定位问题。
心得体会
我把整个项目分成了几个小模块,比如时钟生成、彩灯控制、定时功能和蜂鸣器控制。这样每个模块的功能都比较明确,方便后续调试。FPGA设计中时序控制较困难,尤其是当多个任务同时进行的时候。比如时钟在走,彩灯在亮,蜂鸣器还要响,这些任务都得同步进行。为了解决这个问题,我用了时钟分频和状态机来协调各个模块的工作。虽然一开始有点复杂,但调试几次后,发现只要把时序设计好,整个系统跑起来就很顺畅了。总的来说,这个项目让我对FPGA的设计有了更深的理解,尤其是如何把复杂的功能拆解成小模块,再通过时序控制把它们整合在一起。通过调试和优化解决问题很有成就感。