寒假在家一起练 项目四
基于小脚丫MAX10M02FPGA综合训练板,实现了可定时时钟、温度计、整点报警、播放音频等功能。
标签
FPGA
zxf
更新2021-02-24
1094

项目实现的功能:

1. 可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟,到整点的时候蜂鸣器报警。

2. 实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;

3. 定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息,且与OLED显示的温度值一致;

4. PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART手动发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;

5. 音频文件播放完毕,OLED开始更新时间信息和当前的温度信息

项目总体框图及设计思路:

FqaYEcihtuAEoJdS1KtRWAaigeA1

        从已知需求出发,逐点突破,首先要点亮板载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”模块是按键输入并消抖模块,实现复位、加减小时及分钟等功能。

FrvL4E8XlVmhH1bmIfB6i-tvbYqs

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

资源占用:

FsPiGvEHjy29TJyP1HherLPfyWFR

 

项目总结:

      当初参加电子森林的第一期活动后,感觉意犹未尽,于是在寒假期间上车了本次活动,本以为项目简单,没有太大难度,平常在学校做项目也没有遇到太大的问题,于是心生骄傲自满的情绪,而却在进行时处处遇坑,一个小小的错误往往查找很久。一言以蔽之,磨刀不误砍柴工,万丈高楼平地起,想做好项目必须脚踏实地,从基础的底层出发才行。

附件下载
oled12832_最终版.zip
团队介绍
长春大学/电气工程及其自动化
团队成员
赵肖飞
喜欢捣鼓各种单片机,对各种有趣的科学技术都感兴趣。
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号