内容介绍
内容介绍
一、项目需求
- 实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
- 实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
- 定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
- PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
- 音频文件播放完毕,OLED开始更新时间信息和当前的温度信息。
二、实现思路
本项目将实验的内容分为模块化处理,不同的模块对应不同的要求与功能,其中包含时间生成模块,温度传感器模块,蜂鸣器发声模块,OLED配置模块以及串口通信模块,并最终通过一个顶层模块的编写完成各项功能的协同。
- 顶层模块
module system_top(
input clk_in,
input rst_n_in,
input [2:0] key,
output reg en,
inout one_wire,
input rs232_rx,
output rs232_tx,
output piano_out,
output oled_csn,
output oled_rst,
output oled_dcn,
output oled_clk,
output oled_dat,
output [6:0] seg_led_1,
output [6:0] seg_led_2
);
wire [11:0] tamp_data;
wire [23:0] time_data;
wire flag;
wire play_done;
always @ ( posedge clk_in or negedge rst_n_in )
if ( ~rst_n_in )
en <= 1'b1;
else if ( flag )
en <= 1'b0;
else if ( play_done )
en <= 1'b1;
//时间生成
time_generator time_generator_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.control (key ),
.en (en ),
.time_out (time_data ),
.flag (flag )
);
//温度传感器
DS18B20Z DS18B20Z_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.en (en ),
.one_wire (one_wire ),
.tamp_out (tamp_data )
);
//OLED
OLED12832 OLED12832_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.time_data (time_data[23:8] ),
.tamp_data (tamp_data ),
.oled_csn (oled_csn ),
.oled_rst (oled_rst ),
.oled_dcn (oled_dcn ),
.oled_clk (oled_clk ),
.oled_dat (oled_dat )
);
wire rx_done;
wire [9:0] data_length;
wire [959:0] data_buffer;
//串口
uart_top #
(
.BPS_PARA (1250 )
)
uart_top_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.flag (flag ),
.tamp_data (tamp_data ),
.rs232_rx (rs232_rx ),
.rs232_tx (rs232_tx ),
.data_length (data_length ),
.data_buffer (data_buffer ),
.rx_done (rx_done )
);
//蜂鸣器发声
beeper_control beeper_control_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.en (~en ),
.rx_done (rx_done ),
.data_length (data_length ),
.data_buffer (data_buffer ),
.piano_out (piano_out ),
.play_done (play_done )
);
//数码管
segment segment_inst(
.seg_data_1 (time_data[7:4] ),
.seg_data_2 (time_data[3:0] ),
.seg_led_1 (seg_led_1 ),
.seg_led_2 (seg_led_2 )
);
endmodule
2.时钟模块
时钟模块的主要功能是产生时分秒,并且在整点时改变flag标志位的状态,向蜂鸣器传递信号,从而达到整点报时的效果。
module time_generator(
input clk_in , //12M
input rst_n_in ,
input [2:0] control ,
input en ,
output [23:0] time_out,
output reg flag
);
wire clk_in_5hz;
reg [23:0] cnt_5hz;
always@(posedge clk_in or negedge rst_n_in)
if(!rst_n_in)
cnt_5hz <= 24'd0;
else if(cnt_5hz == 24'd2399999)
cnt_5hz <= 24'd0;
else if( en )
cnt_5hz <= cnt_5hz + 1'b1;
assign clk_in_5hz = (cnt_5hz == 24'd2000000)?1'b1:1'b0;
reg [2:0] cnt;
always @ ( posedge clk_in or negedge rst_n_in )
if (~rst_n_in)
cnt <= 3'd0;
else if ( cnt == 3'd4 && cnt_5hz == 24'd2399999)
cnt <= 3'd0;
else if (cnt_5hz == 24'd2399999)
cnt <= cnt + 1'b1;
reg [7:0] time_shi;
reg [7:0] time_fen;
reg [7:0] time_miao;
//时
always @ ( posedge clk_in_5hz or negedge rst_n_in )
if (~rst_n_in)
time_shi <= 8'd0;
else if( en && ((time_fen[7:4]==4'd5 && time_fen[3:0]==4'd9 && time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9 && cnt == 3'd4) || ~control[2]) )begin
if( time_shi[7:4]==4'd2 && time_shi[3:0]==4'd3 )
time_shi <= 8'd0;
else if ( time_shi[3:0]==4'd9 ) begin
time_shi[7:4] <= time_shi[7:4] + 1'b1;
time_shi[3:0] <= 4'd0;
end
else
time_shi[3:0] <= time_shi[3:0] + 1'b1;
end
//分
always @ ( posedge clk_in_5hz or negedge rst_n_in )
if (~rst_n_in)
time_fen <= 8'd0;
else if( en && ((time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9 && cnt == 3'd4) || ~control[1]) )begin
if( time_fen[7:4]==4'd5 && time_fen[3:0]==4'd9 )
time_fen <= 8'd0;
else if ( time_fen[3:0]==4'd9 ) begin
time_fen[7:4] <= time_fen[7:4] + 1'b1;
time_fen[3:0] <= 4'd0;
end
else
time_fen[3:0] <= time_fen[3:0] + 1'b1;
end
//秒
always @ ( posedge clk_in_5hz or negedge rst_n_in )
if (~rst_n_in)
time_miao <= 8'd0;
else if ( en && (cnt == 3'd4 || ~control[0])) begin
if( time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9)
time_miao <= 8'd0;
else if ( time_miao[3:0]==4'd9 ) begin
time_miao[7:4] <= time_miao[7:4] + 1'b1;
time_miao[3:0] <= 4'd0;
end
else
time_miao[3:0] <= time_miao[3:0] + 1'b1;
end
assign time_out = {time_shi,time_fen,time_miao};
always @ ( posedge clk_in or negedge rst_n_in )
if ( ~rst_n_in )
flag <= 1'b0;
else if ( flag == 1'b1 )
flag <= 1'b0;
else if (time_fen==8'd0&&time_miao==8'd0&&cnt==3'd0&&cnt_5hz == 24'd2399999)
flag <= 1'b1;
endmodule
3.蜂鸣器发声模块
蜂鸣器接收到串口传来的音频信号后,完成控制蜂鸣器发出相应的音色功能,借鉴了一定的FPGA社区中的开源代码实现了相关的功能
module beeper_control(
input clk_in , //12M
input rst_n_in ,
input en ,
input rx_done ,
input [9:0] data_length ,
input [959:0] data_buffer ,
output piano_out ,
output play_done
);
reg [23:0] cnt;
always@(posedge clk_in or negedge rst_n_in)
if(~rst_n_in)
cnt <= 24'd0;
else if(cnt == 24'd3999999)
cnt <= 24'd0;
else
cnt <= cnt + 1'b1;
reg [9:0] data_length_reg;
always@(posedge clk_in or negedge rst_n_in)
if(~rst_n_in)
data_length_reg <= 10'd0;
else if ( rx_done )
data_length_reg <= data_length;
else if ( data_length_reg && cnt == 24'd3999999)
data_length_reg <= data_length_reg - 1'b1;
reg [959:0] data_buffer_reg;
always@(posedge clk_in or negedge rst_n_in)
if(~rst_n_in)
data_buffer_reg <= 960'd0;
else if ( rx_done )
data_buffer_reg <= data_buffer;
else if ( data_length_reg && cnt == 24'd3999999) begin
data_buffer_reg <= data_buffer_reg>>8;
end
wire [4:0] tone;
assign tone = data_buffer_reg[4:0];
wire play_en;
assign play_en = (data_length_reg == 10'd0)?1'b0:1'b1;
Beeper Beeper_inst(
.clk_in (clk_in ),
.rst_n_in (rst_n_in ),
.tone_en (play_en ),
.tone (tone ),
.piano_out (piano_out )
);
assign play_done = ( data_length_reg == 10'd1 && en)?1'b1:1'b0;
endmodule
4.OLED模块
OLED显示模块参考了电子森林使用的应用案例及参考代码完成了相关的内容,故不再赘述。
5.温度传感器DS18B20Z模块
温度传感器模块使用FPGA社区的相关资源以及在同学们的相互帮助下完成了配置,但是此处仅展示对于温度信号的处理部分。
always @ (posedge clk_in_1mhz or negedge rst_n_in)
if ( ~rst_n_in )
tamp_out <= 12'd0;
else if (en) begin
case(data_out[2:0])
3'b000: tamp_out[3:0] <= (data_out[3])?4'd5:4'd0;
3'b001,3'b010: tamp_out[3:0] <= (data_out[3])?4'd6:4'd1;
3'b011: tamp_out[3:0] <= (data_out[3])?4'd7:4'd2;
3'b100,3'b101: tamp_out[3:0] <= (data_out[3])?4'd8:4'd3;
3'b110,3'b111: tamp_out[3:0] <= (data_out[3])?4'd9:4'd4;
endcase
case(data_out[7:4])
4'b0000: begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd6:4'd0; end
4'b0001: begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd7:4'd1; end
4'b0010: begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd8:4'd2; end
4'b0011: begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd9:4'd3; end
4'b0100: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd0:4'd4; end
4'b0101: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd1:4'd5; end
4'b0110: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd2:4'd6; end
4'b0111: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd3:4'd7; end
4'b1000: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd4:4'd8; end
4'b1001: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd5:4'd9; end
4'b1010: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd6:4'd0; end
4'b1011: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd7:4'd1; end
4'b1100: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd8:4'd2; end
4'b1101: begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd9:4'd3; end
4'b1110: begin tamp_out[11:8] <= (data_out[8])?4'd3:4'd1; tamp_out[7:4] <= (data_out[8])?4'd0:4'd4; end
4'b1111: begin tamp_out[11:8] <= (data_out[8])?4'd3:4'd1; tamp_out[7:4] <= (data_out[8])?4'd1:4'd5; end
endcase
end
endmodule
6.串口通信模块
借鉴了STEP FPGA开源社区的相关内容,在完成对于UART信号的处理后,产生脉冲信号,将其送入蜂鸣器模块并产生声音。
//接受音符并产生脉冲
wire ready;
always @ ( posedge clk_in or negedge rst_n_in )
if ( ~rst_n_in )
data_length <= 10'd0;
else if ( rx_done )
data_length <= 10'd0;
else if ( ready && rx_data==8'h1f)
data_length <= data_length + 1'b1;
else if ( ready )
data_length <= data_length + 1'b1;
always @ ( posedge clk_in or negedge rst_n_in )
if ( ~rst_n_in )
data_buffer <= 960'd0;
else if ( rx_done )
data_buffer <= 960'd0;
else if ( ready && rx_data==8'h1f)
data_buffer <= data_buffer;
else if ( ready ) begin
data_buffer[7:0] <= rx_data;
data_buffer[959:8] <= data_buffer[951:0];
end
always @ (posedge clk_in or negedge rst_n_in)
if(!rst_n_in)
rx_done <= 1'b0;
else if (rx_done)
rx_done <= 1'b0;
else if ( ready && rx_data==8'h1f)
rx_done <= 1'b1;
三、遇到的主要的问题
- 初次接触此系列的FPGA,有一些生疏,在电子森林课程的帮助下步上正轨。
- 相关硬件的配置有一定的苦难,在查看学习开源社区的内容后成功解决。
- 上位机最终编写python代码实现,使用了其相当方便的库serial。
四、资源使用情况
五、项目总结
未来不知道是否还有机会使用该开发板完成进一步的开发,电子森林为我们提供了丰富的开源社区资源,这对于开发以及学习使用都有相当巨大的帮助,实践是最好的老师,在开源实例的帮助下我们能快速的硬件的配置及相关功能的实现,收获颇丰。在上位机的编写上,许多同学也是大显神通,在同学们的友好交流与互帮互助下完成了项目的所有内容,感谢电子森林提供的这次与众多热爱FPGA开发的同学同聚一堂的机会。
附件下载
swj.py
上位机文件,需要用python打开上位机文件(具体按照视频演示操作即可)
system_impl1.jed
可用于验证的工程文件
团队介绍
北京理工大学信息与电子学院,共同合作的FPGA团队
团队成员
韩旭
北京理工大学信息与电子学院在读大三学生,喜好进行嵌入式开发等相关工作。
柯洋
北京理工大学信息与电子学院在读大三学生
张千龙
北京理工大学信息与电子学院在读大三学生
评论
0 / 100
查看更多