内容介绍
内容介绍
一,项目要求
基于包含Lattice版本的小脚丫FPGA综合训练板(STEP-MXO2)完成以下基本功能:
1.实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,持续30秒;
2.实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
3.定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息,要与OLED显示的温度值一致;
4.PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
5.音频文件播放完毕,OLED开始更新时间信息和当前的温度信息
二,项目图片展示
原理图:
实物图:
串口助手图:
资源占用:
三,部分代码展示:
1.top模块是顶层模块,是实现各个功能的控制中心,通过在top模块里调用各个模块功能并进行组合调用,从而实现完整的功能。
module top(
input clk,
input hour, //时钟
input minj,
input mins,
input rst_n,
input uart_input, //uart
input uart_on,
inout tone_en,
inout one_wire,
output uart_output,
output beeper
output oled_csn, //oled
output oled_rst,
output oled_dcn,
output oled_clk,
output oled_dat,
);
wire[15:0] d_out;
wire[3:0] temp1
wire[3:0] temp2
wire[3:0] temp3
wire[3:0] temp4
wire[3:0] hour1
wire[3:0] hour2
wire[3:0] min1
wire[3:0] min2
wire[3:0] sec1
wire[3:0] sec2
wire flag;
//温度模块
DS18B20Z DS18B20Z_v1(
.clk_in(clk),
.rst_n_in(rst_n),
.one_wire(one_wire),
.data_out(d_out)
);
//OLED显示
OLED12832 OLED12832_v1(
.clk(clk),
.rst_n(rst_n),
.temp1(temp1),
.temp2(temp2),
.temp3(temp3),
.temp4(temp4),
.hour1(hour1),
.hour2(hour2),
.min1(min1),
.min2(min2),
.sec1(sec1),
.sec2(sec2),
.oled_clk(oled_clk),
.oled_csn(oled_csn),
.oled_dat(oled_dat),
.oled_dcn(oled_dcn),
.oled_rst(oled_rst)
);
wire clk_1h;
clk_1h clk_1h_v1(
.clk(clk),
.rst(rst_n),
.clkout(clk_1h)
);
//时钟
clock clock_v1(
.clk1(clk_1h),
.rst_n(rst_n),
.mins(mins),
.minj(minj),
.hour(hour),
.flag(flag),
.hour1(hour1),
.hour2(hour2),
.min1(min1),
.min2(min2),
.sec1(sec1),
.sec2(sec2)
);
//串口
uartfa uartfa_v1(
.clk_in(clk),
.uart_sw(uart_on),
.temp1(temp1),
.temp3(temp3),
.temp4(temp4),
.hour1(hour1),
.hour2(hour2),
.min1(min1),
.min2(min2),
.sec1(sec1),
.sec2(sec2),
.uart_output(uart_output)
);
wire[7:0] d_uart;
uartshou uartshou_v1(
.clk(clk),
.rst_n(rst_n),
.uart_input(uart_input),
.uart_en(uart_en),
.uart_data(d_uart)
);
//蜂鸣器
beeper beeper_v1(
.clk(clk),
.clk_1h(clk_1h),
.rst_n(rst_n),
.tone_en(tone_en),
.min1(min1),
.min2(min2),
.sec1(sec1),
.uart_en(uart_en),
.uart_data(d_uart),
.trflag(flag),
.beeper(beeper)
);
endmodule
二,时间按键控制模块模块:
begin
if(hour2_q > 0)
hour2_q <= hour2_q - 1;
else//five_now = 0
begin
if(hour1_q == 2)
begin
hour2_q <= 9;
hour1_q <= 1;
end
else if(hour1_q == 1)
begin
hour2_q <= 9;
hour1_q <= 0;
end
else//if(six_now == 0)
begin
hour2_q <= 3;
hour1_q <= 2;
end
end
end
3'b101://分钟增加
begin
if(min2_q > 0)
min2_q <= min2_q - 1;
else
begin
min2_q <= 9;
if(min1_q> 0)
min1_q <= min1_q - 1;
else
min1_q<= 5;
end
end
default://分钟减少
begin
if(min2_q < 9)
min2_q <= min2_q + 1;
else
begin
min2_q <= 0;
if(min1_q < 5)
min1_q <= min1_q + 1;
else
min1_q <= 0;
end
end
endcase
end
end
三:温度模块
温度模块使用电子森林的模板
module temperature
(
input clk_in,
input rst_n_in,
inout one_wire,
output reg [15:0] data_out
);
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam INIT = 3'd2;
localparam WRITE = 3'd3;
localparam READ = 3'd4;
localparam DELAY = 3'd5;
reg [2:0] cnt;
reg one_wire_buffer;
reg [3:0] cnt_main;
reg [7:0] data_wr;
reg [7:0] data_wr_buffer;
reg [2:0] cnt_init;
reg [19:0] cnt_delay;
reg [19:0] num_delay;
reg [3:0] cnt_write;
reg [2:0] cnt_read;
reg [15:0] temperature;
reg [7:0] temperature_buffer;
reg [2:0] state = IDLE;
reg [2:0] state_back = IDLE;
reg clk_1mhz;
reg [2:0] cnt_1mhz;
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
cnt_1mhz <= 3'd0;
clk_1mhz <= 1'b0;
end else if(cnt_1mhz >= 3'd5) begin
cnt_1mhz <= 3'd0;
clk_1mhz <= ~clk_1mhz;
end else begin
cnt_1mhz <= cnt_1mhz + 1'b1;
end
end
always@(posedge clk_1mhz or negedge rst_n_in) begin
if(!rst_n_in) begin
state <= IDLE;
state_back <= IDLE;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
temperature <= 16'h0;
end else begin
case(state)
IDLE:begin
state <= MAIN;
state_back <= MAIN;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
end
MAIN:begin
if(cnt_main >= 4'd11) cnt_main <= 1'b0;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
4'd0: begin state <= INIT;
end
4'd1: begin data_wr <= 8'hcc;state <= WRITE;
end
4'd2: begin data_wr <= 8'h44;state <= WRITE;
end
4'd3: begin num_delay <= 20'd750000;state <= DELAY;state_back <= MAIN;
end
4'd4: begin state <= INIT;
end
4'd5: begin data_wr <= 8'hcc;state <= WRITE;
end
4'd6: begin data_wr <= 8'hbe;state <= WRITE;
end
4'd7: begin state <= READ;
end
4'd8: begin temperature[7:0] <= temperature_buffer;
end
4'd9: begin state <= READ;
end
4'd10: begin temperature[15:8] <= temperature_buffer;
end
4'd11: begin state <= IDLE;data_out <= temperature;
end
default: state <= IDLE;
endcase
end
INIT:begin
if(cnt_init >= 3'd6) cnt_init <= 1'b0;
else cnt_init <= cnt_init + 1'b1;
case(cnt_init)
3'd0: begin one_wire_buffer <= 1'b0;
end
3'd1: begin num_delay <= 20'd500;state <= DELAY;state_back <= INIT;
end
3'd2: begin one_wire_buffer <= 1'bz;
end
3'd3: begin num_delay <= 20'd100;state <= DELAY;state_back <= INIT;
end
3'd4: begin if(one_wire) state <= IDLE; else state <= INIT;
end
3'd5: begin num_delay <= 20'd400;state <= DELAY;state_back <= INIT;
end
3'd6: begin state <= MAIN;
end
default: state <= IDLE;
endcase
end
WRITE:begin
if(cnt <= 3'd6) begin
if(cnt_write >= 4'd6) begin cnt_write <= 1'b1; cnt <= cnt + 1'b1;
end
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt;
end
end else begin
if(cnt_write >= 4'd8) begin cnt_write <= 1'b0; cnt <= 1'b0;
end
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt;
end
end
case(cnt_write)
4'd0: begin data_wr_buffer <= data_wr;
end
4'd1: begin one_wire_buffer <= 1'b0;
end
4'd2: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE;
end
4'd3: begin one_wire_buffer <= data_wr_buffer[cnt];
end
4'd4: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE;
end
4'd5: begin one_wire_buffer <= 1'bz;
end
4'd6: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE;
end
//back to main
4'd7: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE;
end
4'd8: begin state <= MAIN;
end
default: state <= IDLE;
endcase
end
READ:begin
if(cnt <= 3'd6) begin
if(cnt_read >= 3'd5) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1;
end
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt;
end
end else begin
if(cnt_read >= 3'd6) begin cnt_read <= 1'b0; cnt <= 1'b0;
end
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt;
end
end
case(cnt_read)
3'd0: begin one_wire_buffer <= 1'b0;
end
3'd1: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ;
end
3'd2: begin one_wire_buffer <= 1'bz;
end
3'd3: begin num_delay <= 20'd5;state <= DELAY;state_back <= READ;
end
3'd4: begin temperature_buffer[cnt] <= one_wire;
end
3'd5: begin num_delay <= 20'd60;state <= DELAY;state_back <= READ;
end
//back to main
3'd6: begin state <= MAIN;
end
default: state <= IDLE;
endcase
end
DELAY:begin
if(cnt_delay >= num_delay) begin
cnt_delay <= 1'b0;
state <= state_back;
end else cnt_delay <= cnt_delay + 1'b1;
end
endcase
end
end
assign one_wire = one_wire_buffer;
endmodule
4.蜂鸣器乐谱设定代码。
这里我选用了天空之城这首音乐,虽然有点跑调。
parameter TIME = 3000000;
parameter
//低音
L1 = 16'd22935,
L2 = 16'd20428,
L3 = 16'd18203,
L4 = 16'd17181,
L5 = 16'd15305,
L6 = 16'd13635,
L7 = 16'd12147,
//中音
M1 = 16'd11464,
M2 = 16'd10215,
M3 = 16'd9100,
M4 = 16'd8589,
M5 = 16'd7652,
M6 = 16'd6817,
M7 = 16'd6073,
//高音
H1 = 16'd5740,
H2 = 16'd5107,
H3 = 16'd4549,
H4 = 16'd4294,
H5 = 16'd3825,
H6 = 16'd3408,
H7 = 16'd3036;
assign beeper = beep; //输出
assign trflag = tr;
reg[16:0]count,count_end,count_end1;
reg[23:0]count1;
reg[7:0]state;
always@(posedge clk)
begin
if(tone_en && min1 == 0 && min2 == 0 && sec1 < 3)
begin
count <= count + 1'b1;
if(count == count_end)
begin
count <= 17'h0;
beep <= !beep;
end
end
else if(tone_en && !tr)
begin
count <= count + 1'b1;
if(count == count_end1)
begin
count <= 17'h0;
beep <= !beep;
end
end
end
//设定乐谱为音乐天空之城
always @(posedge clk)
begin
if(count1 < TIME)
count1 = count1 + 1'b1;
else
begin
count1 = 24'd0;
if(state == 8'd63)
state = 8'd0;
else
state = state + 1'b1;
case(state)
8'd0:count_end=M6;
8'd1:count_end=M7;
8'd2:count_end=H1;
8'D3:count_end=M7;
8'D4:count_end=H1;
8'D5:count_end=H3;
8'D6:count_end=M7;
8'D7:count_end=M3;
8'D8:count_end=M3;
8'D9:count_end=M6;
8'D10:count_end=M5;
8'D11:count_end=M6;
8'D12:count_end=H1;
8'D14:count_end=M5;
8'D15:count_end=L1;
8'D16:count_end=M6;
8'D17:count_end=M6;
8'D18:count_end=M4;
8'D19:count_end=M3;
8'D20:count_end=M4;
8'D21:count_end=M1;
8'D22:count_end=M3;
8'D23:count_end=H1;
8'D24:count_end=H1;
8'D25:count_end=H1;
8'D26:count_end=M7;
8'D27:count_end=M4;
8'D28:count_end=M4;
8'D29:count_end=M7;
8'D30:count_end=M7;
8'D31:count_end=L1;
8'd32:count_end=M6;
8'd33:count_end=M7;
8'd34:count_end=L1;
8'D35:count_end=M7;
8'D36:count_end=H1;
8'D37:count_end=H3;
8'D38:count_end=M7;
8'D39:count_end=L1;
8'D40:count_end=M3;
8'D42:count_end=M3;
8'D43:count_end=M6;
8'D44:count_end=M5;
8'D45:count_end=M6;
8'D46:count_end=H1;
8'D47:count_end=M5;
8'D48:count_end=M1;
8'D49:count_end=M1;
8'D50:count_end=M3;
8'D51:count_end=M4;
8'D52:count_end=H1;
8'D53:count_end=M7;
8'D54:count_end=M1;
8'D55:count_end=H1;
8'D56:count_end=H2;
8'D57:count_end=H2;
8'D58:count_end=H3;
8'D59:count_end=H1;
8'D60:count_end=H1;
8'D61:count_end=M7;
8'D62:count_end=M6;
8'D63:count_end=M6;
default: count_end = 16'h0;
endcase
end
五,总结
我本人对于FPGA完全不了解,之前也没有接触过verilog的语法编写,所以代码写的磕磕碰碰的,幸好在这个平台有许多同伴,一些我实在看不懂的,比如串口通信这一部分,通过借鉴他们的成果,才实现要求的功能,最后才完成了这个项目。希望以后可以熟练掌握这些知识,最终能够写出来自己的代码。
软硬件
附件下载
xiangmu.jed
团队介绍
北京理工大学,信息与电子学院
团队成员
张乙凡
来自北理工的一个普通学生
评论
0 / 100
查看更多