项目实现的功能:
1. 可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟,到整点的时候蜂鸣器报警。
2. 实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
3. 定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息,且与OLED显示的温度值一致;
4. PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART手动发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
5. 音频文件播放完毕,OLED开始更新时间信息和当前的温度信息
项目总体框图及设计思路:
从已知需求出发,逐点突破,首先要点亮板载0.91寸oled屏幕,然后显示时间温度等信息,由于初次接触Verilog语言,所以四处找例程借鉴,最后借鉴了中景园的参考代码,突破oled模块。然后是温度计功能,使用板载DS18B20温度传感器,因为在stm上经常使用18B20测温,对其单总线工作原理比较熟悉,问题不大。按键部分参考电子森林按键消抖程序,蜂鸣器播放音频之前在stm32上做过实验,设置好PWM频率对应的音调后,参考电子森林网站的beep部分,可以将曲谱播放出来,最难的是uart部分,不仅需要发送信息,而且需要接受上位机的曲谱,经过各路大神指点,参考CSDN的博客,最后还是突破了。
遇到的难题及解决办法:
1,语法不通,不要着急做项目,先看看入门基础例程,从点灯开始。
2,软件不熟悉,开发流程不通,多观看电子森林的直播视频,跟着一步一步做。
3,出错看不懂,不知道是哪里的错误,翻译英文,然后根据对其他单片机的开发经验理解这个错误,去百度或者CSDN上搜索这个错误,在交流群里询问其他玩家。
4,新的模块不会用,去电子森林,什么资料都有。
核心部分代码:
项目顶层文件为 “top”,模块名也是“top”,顶层文件包含三大模块,如上文的程序框图所示。其中“uart_seg”为串口模块,串口模块实现收发uart信息的功能,当收到上位机的曲谱后,该模块驱动蜂鸣器“Beeper”模块,总而播放音频,同时实现温度信息的转换,将温度信息传递到top模块,然后经top模块传递到oled屏幕。oled12832模块实现时间温度的数字显示,用户设置时钟的交互。“debounce”模块是按键输入并消抖模块,实现复位、加减小时及分钟等功能。
1.top 顶层代码
module top (
input [2:0] key,
input mode,
input clk, //12MHz系统时钟
input rst_n, //复位
input fpga_rx, //UART接收输入
inout one_wire, // ds18b20z one-wire-bus
output led,
output oled_csn, //OLCD使能
output oled_rst, //OLCD复位
output oled_dcn, //OLCD数据指令控制
output oled_clk, //oled时钟
output oled_dat, //OLCD数据
output fpga_tx, //UART发送输出
output beeper
);
reg flag; //秒信号
reg [23:0] cnt;
reg [5:0] hour;
reg [5:0] minute;
reg [5:0] second;
wire [5:0] temper;
wire [2:0] key_pulse;
//wire clk_60m,clk_1m;
//key_pulse[0] 左数第二个键
wire stop;
reg stop_reg;//温度停止更新
/****************串口变量***************/
wire tx_flag; //uart的复位信号 发送一次
wire tx_pulse;
reg tx_reg; //三个控制量
wire [24:0] temp; //温度
wire continue;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n) begin cnt <= 20'b0; second <= 6'b0; minute <= 6'b0; hour <= 6'b0; tx_reg<= 0; end
else
if(mode) //手动模式,校准时间
begin
flag <= flag; //手动模式,停止计时
if (key_pulse[0]) // 小时加
begin
if(hour < 23) hour <= hour + 1'b1;
else hour <= 6'd0;
end
else if (key_pulse[1]) // 分钟加
begin
if(minute<59) minute <= minute + 1'b1;
else minute <= 6'd0;
end
else if (key_pulse[2]) // 分钟减
begin
if(minute>0) minute <= minute - 1'b1;
else minute <= 6'd59;
end
else
begin hour <= hour; minute <= minute; end
end
else //自动模式,计时
begin
if(continue) begin
stop_reg <= 1'b0;
if(cnt== 24'd11_999_999)
begin
second <= second + 1'b1;
cnt <= 1'b0;
flag <= ~flag;
tx_reg<= 0;
end
else if(second >= 60)
begin
second <= 6'b0;
minute <= minute + 1'b1;
end
else if(minute >= 60) begin
minute <= 6'b0;
hour <= hour + 1'b1;
tx_reg<=1'b1; //**********蜂鸣器报警,上传温度信息
end
else if (hour >= 24)
hour <= 0;
else cnt <= cnt + 1'b1;
end
else begin
stop_reg <= 1'b1;
tx_reg<= 0;
end
end
end
assign led = flag;
assign tx_flag = tx_reg;
assign stop = stop_reg;
debounce #(.N(3)) u2 ( //N为消抖按键数 默认N=1
.clk (clk),
.rst (rst_n),
.key (key),
.key_pulse (key_pulse)
);
OLED12832 u4 (
.clk(clk),
.rst_n(rst_n),
.stop(stop),
.hour(hour),
.minute(minute),
.second(flag),
.temper(temp),
.oled_csn(oled_csn),
.oled_rst(oled_rst),
.oled_dcn(oled_dcn),
.oled_clk(oled_clk),
.oled_dat(oled_dat)
);
uart_seg u5
(
.clk(clk), //系统时钟
.rst_n(rst_n), //复位,低有效
.tx_flag(tx_flag), //蜂鸣器报警,上传温度
.fpga_rx(fpga_rx), //UART接收引脚
.one_wire(one_wire), // ds18b20z one-wire-bus
.temper(temp),
.fpga_tx(fpga_tx), //UART_TX引脚
.beeper(beeper),
.continue(continue)
);
endmodule
2.uart部分
module uart_seg
(
input clk, //系统时钟
input rst_n, //系统复位,低有效
inout tx_flag,
input fpga_rx, //UART接收输入引脚
inout one_wire, // ds18b20z one-wire-bus
output [24:0] temper,
output fpga_tx, //UART发送输出引脚
output beeper,
output reg continue
);
wire [15:0] data_out;
DS18B20Z DS18B20Z_uut
(
.clk (clk ), // system clock
.rst_n (rst_n ), // system reset, active low
.one_wire (one_wire ), // ds18b20z one-wire-bus
.data_out (data_out ) // ds18b20z data_out
);
wire temperature_flag = data_out[15:11]? 1'b0:1'b1; //高五位应该是符号位
wire [10:0] temperature_code = temperature_flag? data_out[10:0]:(~data_out[10:0])+1'b1; //温度正负
wire [20:0] bin_code = temperature_code * 16'd625;
wire [24:0] bcd_code; //十位[23:20],个位[19:16],小数位[14:12]
bin_to_bcd bin_to_bcd_uut
(
.rst_n (rst_n ), // system reset, active low
.bin_code (bin_code ), // binary code
.bcd_code (bcd_code ) // bcd code
);
reg [20:0] cnt;
reg tx_data_valid;
reg [7:0] tx_data_in;
wire clk_divide;
wire beep;
wire [3:0] tone;
reg [4:0] t =0;
reg flag_t; //开始音乐
reg beep_reg;
wire rx_data_valid;
wire [7:0] rx_data_out;
reg [7:0] rx_buf [20:0];
reg [3:0] i;
always @ (posedge rx_data_valid or negedge rst_n) begin //***********接受数据
if(!rst_n) begin flag_t <= 1'b0; end
else begin
flag_t <= 1'b1;
i <= i+ 1'b1;
rx_buf[i] <= rx_data_out;
end
end
always @ (posedge clk_divide or negedge rst_n) begin //**********播放音频
if(!rst_n) begin t <= 0; continue <= 1; end
else begin
if(tx_flag) begin beep_reg <=1'b1; continue <= 0; end
else
begin
if(flag_t ) begin t <= t + 1'b1; continue <= 0;beep_reg <= 0; end
else t<= 0;
if(t >= 5'd15 ) begin
continue <= 1;
t <= 5'd16;
end
else ;
end
end
end
always @ (posedge clk or negedge rst_n) begin //***发送温度
if(!rst_n)
begin tx_data_valid <= 1'b0; tx_data_in <= 1'b0; cnt <= 1'b0; end
else if(cnt == 20'd190_000) ;
else cnt <= cnt + 1'b1;
if (tx_flag) begin
cnt <= 1'b0;
end
else ;
if(cnt[7:0] == 8'd100 && tx_data_valid)
tx_data_valid <= 1'b0;
else begin
case(cnt)
20'd12_500: begin tx_data_in <= 8'hCE; tx_data_valid <= 1'b1;end //温
20'd25_000: begin tx_data_in <= 8'hC2; tx_data_valid <= 1'b1;end
20'd37_500: begin tx_data_in <= 8'hB6; tx_data_valid <= 1'b1;end //度
20'd50_000: begin tx_data_in <= 8'hC8; tx_data_valid <= 1'b1;end
20'd62_500: begin tx_data_in <= 8'h3A; tx_data_valid <= 1'b1;end //:
20'd75_000: begin tx_data_in <= 8'h30+bcd_code[23:20]; tx_data_valid <= 1'b1;end
20'd87_500: begin tx_data_in <= 8'h30+bcd_code[19:16]; tx_data_valid <= 1'b1;end
20'd100_000: begin tx_data_in <= 8'h2E; tx_data_valid <= 1'b1;end //.
20'd112_500: begin tx_data_in <= 8'h30+bcd_code[15:12]; tx_data_valid <= 1'b1;end
20'd125_000: begin tx_data_in <= 8'h0D; tx_data_valid <= 1'b1;end //\r
20'd137_500: begin tx_data_in <= 8'h0A; tx_data_valid <= 1'b1;end //\n
default:begin tx_data_in <= tx_data_in; tx_data_valid <= tx_data_valid;end
endcase
end
end
assign temper = bcd_code;
assign tone = rx_buf[t] [3:0];
assign beep = beep_reg;
Uart_Bus u1
(
.clk (clk ), //系统时钟 12MHz
.rst_n (rst_n ), //系统复位,低有效
.uart_rx (fpga_rx ), //UART接收输入
.rx_data_valid (rx_data_valid ), //接收数据有效脉冲
.rx_data_out (rx_data_out ), //接收到的数据
.tx_data_valid (tx_data_valid ),
.tx_data_in (tx_data_in ),
.uart_tx (fpga_tx )
);
divide #(.WIDTH(24),.N(6000000)) u2 (
.clk (clk),
.rst_n (rst_n),
.clkout (clk_divide)
);
Beeper u3
(
.clk(clk),
.rst_n(rst_n),
.beep(beep),
.tone(tone),
.beeper(beeper)
);
endmodule
资源占用:
项目总结:
当初参加电子森林的第一期活动后,感觉意犹未尽,于是在寒假期间上车了本次活动,本以为项目简单,没有太大难度,平常在学校做项目也没有遇到太大的问题,于是心生骄傲自满的情绪,而却在进行时处处遇坑,一个小小的错误往往查找很久。一言以蔽之,磨刀不误砍柴工,万丈高楼平地起,想做好项目必须脚踏实地,从基础的底层出发才行。