2021寒假在家一起练项目-赵新语
介绍基于FPGA和DS18B20数字温度传感器的温度测量系统。以FPGA为开发平台,利用单总线数字温度传感器DS18B20测量温度值,通过FPGA实现温度值的采集和驱动具有良好用户界面的oled实时显示测量的温度值和时间。
标签
FPGA
温度报警装置
朝辞白
更新2021-02-21
932

项目需求

本项目为基于Altera MAX10 FPGA的一个完成定时、测温、报警、控制的小项目,具体功能如下

1.实现一个可定时时钟的功能,用4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
2.实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
3.定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
4.PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
5.音频文件播放完毕,OLED开始更新时间信息和当前的温度信息

项目基本功能

1.显示时钟计时

2.pwm通过三极管驱动蜂鸣器整点报时和报警

3.ds18b20实现温度读取

4.uart串口通信

5.oled显示时间和温度

6.代码整合实现整体功能

 

FveKCiRVMm4jy2CftVFirRgAKMSH

项目RTL视图FngUfM0oqB9fgOD0gr1MZyH1Lt9A

关键代码

1.闹钟设置

第一个按键按一下进入小时设置,按两下进入分钟设置,第二个按键和第三个按键进行加或者减,第四个按键确认.利用分频器把晶振12M分为1次0.01秒每一百次秒钟加一,秒钟加到60分钟加一再加一个判断如果分钟为0,就开始放音乐

闹钟相关代码:

module alarm
#(

parameter simPresent = 0

)
(

input clk,
input rst_n,

output reg pulse,
output reg [5:0] second,
output [5:0] hour_o,
output [5:0] minute_o,


input time_enter,
input time_aug,
input time_minus,
input time_set

);


reg [5:0] hour;
reg [5:0] minute;

reg [23:0] cnt;
reg [5:0] minute_set;
reg [5:0] hour_set;
reg [3:0] state;
reg [3:0] state_next;


localparam ST_IDLE = 0;
localparam ST_SETHOURS = 1;
localparam ST_SETMINUTES = 2;

localparam prescale = simPresent ? 12 : 12000000;
//localparam prescale = simPresent ? 12 : 200000;

always @(posedge clk, negedge rst_n) begin
if(!rst_n) cnt <= 0;
else if(cnt==(prescale-1)) cnt<=0;
else cnt <= cnt+1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) pulse <= 0;
else pulse <= (cnt==(prescale-1));
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) second <= 0;
else if(state!=ST_IDLE) second <= 0;
else if((second==59)&pulse) second <= 0;
else if(pulse) second <= second + 1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) minute_set <= 0;
else if((state==ST_IDLE)&time_enter) minute_set <= minute;
else if((state==ST_SETMINUTES)&time_aug&(minute_set==59)) minute_set <= 0;
else if((state==ST_SETMINUTES)&time_aug) minute_set <= minute_set + 1;
else if((state==ST_SETMINUTES)&time_minus&(minute_set==0)) minute_set <= 59;
else if((state==ST_SETMINUTES)&time_minus) minute_set <= minute_set - 1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) minute <= 0;
else if((state!=ST_IDLE)&time_set) minute <= minute_set;
else if((minute==59)&(second==59)&pulse) minute <= 0;
else if((second==59)&pulse) minute <= minute + 1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) hour_set <= 0;
else if((state==ST_IDLE)&time_enter) hour_set <= hour;
else if((state==ST_SETHOURS)&time_aug&(hour_set==23)) hour_set <= 0;
else if((state==ST_SETHOURS)&time_aug) hour_set <= hour_set + 1;
else if((state==ST_SETHOURS)&time_minus&(hour_set==0)) hour_set <= 23;
else if((state==ST_SETHOURS)&time_minus) hour_set <= hour_set - 1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) hour <= 0;
else if((state!=ST_IDLE)&time_set) hour <= hour_set;
else if((hour==23)&(second==59)&(minute==59)&pulse) hour <= 0;
else if((second==59)&(minute==59)&pulse) hour <= hour + 1;
end

always @(posedge clk, negedge rst_n) begin
if(!rst_n) state <= ST_IDLE;
else state <= state_next;
end


always @(*) begin
case(state)
ST_IDLE: begin if(time_enter) state_next = ST_SETHOURS; else state_next = state; end
ST_SETHOURS: begin if(time_enter) state_next = ST_SETMINUTES; else if(time_set) state_next = ST_IDLE; else state_next = state; end
ST_SETMINUTES: begin if(time_enter) state_next = ST_IDLE; else if(time_set) state_next = ST_IDLE; else state_next = state; end
default: state_next = ST_IDLE;
endcase
end

assign hour_o = (state==ST_IDLE) ? hour : hour_set;
assign minute_o = (state==ST_IDLE) ? minute : minute_set;


endmodule

2.温度计

温度传感器为DS18B20Z芯片。DS18B20数字温度传感器提供9-Bit到12-Bit的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能。DS18B20采用一个数据线(以及地)与微控制器进行通。而且,DS18B20可以直接由数据线供电而不需要外部电源供电。我们把芯片传回来的二进制数对应芯片手册上的对应关系解码出来得到温度传给oled显示.这个芯片测量温度时每750ms测一次,这个时间比较长我们应该把这个时间计算好多久更新一次温度.

温度信息相关代码:

module DS18B20Z
(
input clk, // system clock
input rst_n, // system reset, active low
inout one_wire, // ds18b20z one-wire-bus
output reg [15:0] data_out // ds18b20z 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;

//generate clk_1mhz clock
reg clk_1mhz;
reg [2:0] cnt_1mhz;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) 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

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 [5:0] cnt_write;
reg [5:0] cnt_read;
reg [15:0] temperature;
reg [7:0] temperature_buffer;
reg [2:0] state = IDLE;
reg [2:0] state_back = IDLE;
always@(posedge clk_1mhz or negedge rst_n) begin
if(!rst_n) begin
state <= IDLE;
state_back <= IDLE;
cnt_main <= 4'd0;
cnt_init <= 3'd0;
cnt_write <= 6'd0;
cnt_read <= 6'd0;
cnt_delay <= 20'd0;
one_wire_buffer <= 1'bz;
temperature <= 16'h0;
end else begin
case(state)
IDLE:begin
state <= MAIN;
state_back <= MAIN;
cnt_main <= 4'd0;
cnt_init <= 3'd0;
cnt_write <= 6'd0;
cnt_read <= 6'd0;
cnt_delay <= 20'd0;
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_write >= 6'd50) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
//lock data_wr
6'd0: begin data_wr_buffer <= data_wr; end
//write bit 0
6'd1: begin one_wire_buffer <= 1'b0; end
6'd2: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd3: begin one_wire_buffer <= data_wr_buffer[0]; end
6'd4: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd5: begin one_wire_buffer <= 1'bz; end
6'd6: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 1
6'd7: begin one_wire_buffer <= 1'b0; end
6'd8: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd9: begin one_wire_buffer <= data_wr_buffer[1]; end
6'd10: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd11: begin one_wire_buffer <= 1'bz; end
6'd12: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 2
6'd13: begin one_wire_buffer <= 1'b0; end
6'd14: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd15: begin one_wire_buffer <= data_wr_buffer[2]; end
6'd16: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd17: begin one_wire_buffer <= 1'bz; end
6'd18: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 3
6'd19: begin one_wire_buffer <= 1'b0; end
6'd20: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd21: begin one_wire_buffer <= data_wr_buffer[3]; end
6'd22: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd23: begin one_wire_buffer <= 1'bz; end
6'd24: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 4
6'd25: begin one_wire_buffer <= 1'b0; end
6'd26: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd27: begin one_wire_buffer <= data_wr_buffer[4]; end
6'd28: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd29: begin one_wire_buffer <= 1'bz; end
6'd30: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 5
6'd31: begin one_wire_buffer <= 1'b0; end
6'd32: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd33: begin one_wire_buffer <= data_wr_buffer[5]; end
6'd34: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd35: begin one_wire_buffer <= 1'bz; end
6'd36: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 6
6'd37: begin one_wire_buffer <= 1'b0; end
6'd38: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd39: begin one_wire_buffer <= data_wr_buffer[6]; end
6'd40: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd41: begin one_wire_buffer <= 1'bz; end
6'd42: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//write bit 7
6'd43: begin one_wire_buffer <= 1'b0; end
6'd44: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
6'd45: begin one_wire_buffer <= data_wr_buffer[7]; end
6'd46: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd47: begin one_wire_buffer <= 1'bz; end
6'd48: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
//back to main
6'd49: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
6'd50: begin state <= MAIN; end
default: state <= IDLE;
endcase
end
READ:begin
if(cnt_read >= 6'd48) cnt_read <= 1'b0;
else cnt_read <= cnt_read + 1'b1;
case(cnt_read)
//read bit 0
6'd0: begin one_wire_buffer <= 1'b0; end
6'd1: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd2: begin one_wire_buffer <= 1'bz; end
6'd3: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd4: begin temperature_buffer[0] <= one_wire; end
6'd5: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 1
6'd6: begin one_wire_buffer <= 1'b0; end
6'd7: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd8: begin one_wire_buffer <= 1'bz; end
6'd9: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd10: begin temperature_buffer[1] <= one_wire; end
6'd11: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 2
6'd12: begin one_wire_buffer <= 1'b0; end
6'd13: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd14: begin one_wire_buffer <= 1'bz; end
6'd15: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd16: begin temperature_buffer[2] <= one_wire; end
6'd17: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 3
6'd18: begin one_wire_buffer <= 1'b0; end
6'd19: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd20: begin one_wire_buffer <= 1'bz; end
6'd21: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd22: begin temperature_buffer[3] <= one_wire; end
6'd23: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 4
6'd24: begin one_wire_buffer <= 1'b0; end
6'd25: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd26: begin one_wire_buffer <= 1'bz; end
6'd27: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd28: begin temperature_buffer[4] <= one_wire; end
6'd29: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 5
6'd30: begin one_wire_buffer <= 1'b0; end
6'd31: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd32: begin one_wire_buffer <= 1'bz; end
6'd33: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd34: begin temperature_buffer[5] <= one_wire; end
6'd35: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 6
6'd36: begin one_wire_buffer <= 1'b0; end
6'd37: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd38: begin one_wire_buffer <= 1'bz; end
6'd39: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd40: begin temperature_buffer[6] <= one_wire; end
6'd41: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//read bit 7
6'd42: begin one_wire_buffer <= 1'b0; end
6'd43: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
6'd44: begin one_wire_buffer <= 1'bz; end
6'd45: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
6'd46: begin temperature_buffer[7] <= one_wire; end
6'd47: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
//back to main
6'd48: 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

3.蜂鸣器

蜂鸣器分为两种一种无源一种有源,有源蜂鸣器给高电平就响但只能发出一种声音二无源蜂鸣器外部给一个震荡信号根据震荡信号的不同发出不同的声音.板载蜂鸣器为无源蜂鸣器,通过PWM信号驱动使蜂鸣器发声。通过改变PWM信号分频计数终止控制PWM信号的频率,从而达到改变蜂鸣器发声频率的目的。在整点报时时,放音乐,.在接收到pc端传来的数据时,根据接收到的数据控制蜂鸣器发出尖锐的警报声.

pwm相关代码:

module PWM #
(
parameter WIDTH = 32 //ensure that 2**WIDTH > cycle
)
(
input clk,
input rst_n,
input en,
input [WIDTH-1:0] cycle, //cycle > duty
input [WIDTH-1:0] duty, //duty < cycle
output reg pwm_out
);

reg [WIDTH-1:0] cnt;
//counter for cycle
always @(posedge clk or negedge rst_n)
if(!rst_n) cnt <= 1'b1;
else if(cnt >= cycle) cnt <= 1'b1;
else if(en) cnt <= cnt + 1'b1;

//pulse with duty
always @(posedge clk or negedge rst_n)
if(!rst_n) pwm_out <= 1'b1;
else if(!en) pwm_out <= 0;
else if(cnt < duty) pwm_out <= 1'b1;
else pwm_out <= 1'b0;

endmodule

驱动蜂鸣器相关代码:

module tone
(
input clk,
input rst_n,
input en,
output reg [15:0] cycle
);

// localparam DO = 10215;
// localparam RE = 12148;
// localparam MI = 15306;
// localparam FA = 17180;
// localparam SO = 20432;
// localparam LA = 24296;
// localparam TI = 30612;

localparam DO = 19389;
localparam RE = 17256;
localparam MI = 15358;
localparam FA = 13669;
localparam SO = 12895;
localparam LA = 11477;
localparam TI = 10215;

reg [25:0] cnt;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 1'b0;
else if(!en) cnt <= 0;
else cnt <= cnt + 1'b1;
end

//always@(cnt[25:22]) begin
// case(cnt[25:22])
// 4'd0: cycle = 16'd45872; //L1,
// 4'd1: cycle = 16'd40858; //L2,
// 4'd2: cycle = 16'd36408; //L3,
// 4'd3: cycle = 16'd34364; //L4,
// 4'd4: cycle = 16'd30612; //L5,
// 4'd5: cycle = 16'd27273; //L6,
// 4'd6: cycle = 16'd24296; //L7,
// 4'd7: cycle = 16'd22931; //M1,
// 4'd8: cycle = 16'd20432; //M2,
// 4'd9: cycle = 16'd18201; //M3,
// 4'd10: cycle = 16'd17180; //M4,
// 4'd11: cycle = 16'd15306; //M5,
// 4'd12: cycle = 16'd13636; //M6,
// 4'd13: cycle = 16'd12148; //M7,
// 4'd14: cycle = 16'd11478; //H1,
// 4'd15: cycle = 16'd10215; //H2,
// default: cycle = 16'd0; //cycle为0,PWM占空比为0,低电平
// endcase
//end

always@(cnt[25:22]) begin
case(cnt[25:22])
4'd0: cycle = DO; //L1,
4'd1: cycle = DO; //L2,
4'd2: cycle = SO; //L3,
4'd3: cycle = SO; //L4,
4'd4: cycle = LA; //L5,
4'd5: cycle = LA; //L6,
4'd6: cycle = SO; //L7,
4'd7: cycle = 0; //M1,
4'd8: cycle = FA; //M2,
4'd9: cycle = FA; //M3,
4'd10: cycle = MI; //M4,
4'd11: cycle = MI; //M5,
4'd12: cycle = RE; //M6,
4'd13: cycle = RE; //M7,
4'd14: cycle = DO; //H1,
4'd15: cycle = 0; //H2,
default: cycle = 16'd0; //cycle为0,PWM占空比为0,低电平
endcase
end

endmodule

4.OLED显示屏

OLED显示屏用于显示时间和温度信息。该模块不断读取输入的时间和温度信息。通过读取对应的字模并显示到屏幕上。在收到uart数据时,OLED显示屏处于空闲状态,停止对时间、温度的刷新。在播放完毕后OLED数据继续刷新。

驱动OLED相关代码:

module OLED12832
(
input clk, //12MHz
input rst_n, 

input [15:0] oled1,
input [15:0] oled2,
input [15:0] oled3, 

output reg oled_csn, 
output reg oled_rst, 
output reg oled_dcn, 
output reg oled_clk, 
output reg oled_dat 
);

localparam INIT_DEPTH = 16'd25; 
localparam IDLE = 6'h1, MAIN = 6'h2, INIT = 6'h4, SCAN = 6'h8, WRITE = 6'h10, DELAY = 6'h20;
localparam HIGH = 1'b1, LOW = 1'b0;
localparam DATA = 1'b1, CMD = 1'b0;

reg [7:0] cmd [24:0];
reg [39:0] mem [122:0];
reg [7:0] y_p, x_ph, x_pl;
reg [(8*21-1):0] char;
reg [7:0] num, char_reg; //
reg [4:0] cnt_main, cnt_init, cnt_scan, cnt_write;
reg [15:0] num_delay, cnt_delay, cnt;
reg [5:0] state, state_back;

always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= IDLE; state_back <= IDLE;
end else begin
case(state)
IDLE:begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= MAIN; state_back <= MAIN;
end
MAIN:begin
if(cnt_main >= 5'd8) cnt_main <= 5'd5;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main) //MAIN״̬
5'd0: begin state <= INIT; end
5'd1: begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Time ";state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Temperature ";state <= SCAN; end
5'd4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end

5'd5: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= " "; state <= SCAN; end
5'd6: begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 5; char <= {oled2,":",oled3}; state <= SCAN; end
5'd7: begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= " "; state <= SCAN; end
5'd8: begin y_p <= 8'hb3; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 2; char <= oled1; state <= SCAN; end

default: state <= IDLE;
endcase
end
INIT:begin 
case(cnt_init)
5'd0: begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end 
5'd1: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end 
5'd2: begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end
5'd3: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end 
5'd4: begin
if(cnt>=INIT_DEPTH) begin 
cnt <= 1'b0;
cnt_init <= cnt_init + 1'b1;
end else begin
cnt <= cnt + 1'b1; num_delay <= 16'd5;
oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
end
end
5'd5: begin cnt_init <= 1'b0; state <= MAIN; end 
default: state <= IDLE;
endcase
end
SCAN:begin 
if(cnt_scan == 5'd11) begin
if(num) cnt_scan <= 5'd3;
else cnt_scan <= cnt_scan + 1'b1;
end else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
else cnt_scan <= cnt_scan + 1'b1;
case(cnt_scan)
5'd 0: begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end 
5'd 1: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end 
5'd 2: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end 

5'd 3: begin num <= num - 1'b1;end
5'd 4: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end 
5'd 5: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end 
5'd 6: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end 
5'd 7: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
5'd 8: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
5'd 9: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
5'd10: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
5'd11: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
5'd12: begin state <= MAIN; end
default: state <= IDLE;
endcase
end
WRITE:begin 
if(cnt_write >= 5'd17) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
5'd 0: begin oled_csn <= LOW; end 
5'd 1: begin oled_clk <= LOW; oled_dat <= char_reg[7]; end
5'd 2: begin oled_clk <= HIGH; end
5'd 3: begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
5'd 4: begin oled_clk <= HIGH; end
5'd 5: begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
5'd 6: begin oled_clk <= HIGH; end
5'd 7: begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
5'd 8: begin oled_clk <= HIGH; end
5'd 9: begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
5'd10: begin oled_clk <= HIGH; end
5'd11: begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
5'd12: begin oled_clk <= HIGH; end
5'd13: begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
5'd14: begin oled_clk <= HIGH; end
5'd15: begin oled_clk <= LOW; oled_dat <= char_reg[0]; end 
5'd16: begin oled_clk <= HIGH; end
5'd17: begin oled_csn <= HIGH; state <= DELAY; end 
default: state <= IDLE;
endcase
end

6.串口通信

FjfegG8SL2OcJ7USVssLvjO2gjDE

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART。它将要传输的资料在串口通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片,UART通常被集成于其他通讯接口的连结上。串口通信的通信协议为:1位起始位“0”,8位数据位,长度不固定的停止位“1”。串口通信速率为9600波特。

起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
资料位:紧接着起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
奇偶校验位:资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输效率同时也越慢。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
波特率:是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如传输使用256阶符号,每8bit代表一个符号,资料传送速率为120字符/秒,则波特率就是120baud,比特率是120*8=960bit/s。这两者的概念很容易搞错。

发送:发送时需发送“XX”几个字符,将这几个字符及起始位、终止位存入寄存器中,依次发出。发送时钟为9600Hz。

接收:接收时采用12MHz时钟。在接收到正确的数据后,触发蜂鸣器报警。

串口通信代码

module Baud #
(
parameter BPS_PARA = 1250 //12MHz时钟时参数1250对应9600的波特率
)
(
input clk, //系统时钟
input rst_n, //系统复位,低有效
input bps_en, //接收或发送时钟使能
output reg bps_clk //接收或发送时钟输出
);

reg [12:0] cnt;
//计数器计数满足波特率时钟要求
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 1'b0;
else if((cnt >= BPS_PARA-1)||(!bps_en)) //当时钟信号不使能(bps_en为低电平)时,计数器清零并停止计数
cnt <= 1'b0; //当时钟信号使能时,计数器对系统时钟计数,周期为BPS_PARA个系统时钟周期
else
cnt <= cnt + 1'b1;
end

//产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
bps_clk <= 1'b0;
else if(cnt == (BPS_PARA>>1)) //右移一位等于除以2,终值BPS_PARA为数据更替点,中值数据稳定,做采样点
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end

endmodule

module Baud #
(
parameter BPS_PARA = 1250 //12MHz时钟时参数1250对应9600的波特率
)
(
input clk, //系统时钟
input rst_n, //系统复位,低有效
input bps_en, //接收或发送时钟使能
output reg bps_clk //接收或发送时钟输出
);

reg [12:0] cnt;
//计数器计数满足波特率时钟要求
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 1'b0;
else if((cnt >= BPS_PARA-1)||(!bps_en)) //当时钟信号不使能(bps_en为低电平)时,计数器清零并停止计数
cnt <= 1'b0; //当时钟信号使能时,计数器对系统时钟计数,周期为BPS_PARA个系统时钟周期
else
cnt <= cnt + 1'b1;
end

//产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
bps_clk <= 1'b0;
else if(cnt == (BPS_PARA>>1)) //右移一位等于除以2,终值BPS_PARA为数据更替点,中值数据稳定,做采样点
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end

endmodule

 

通过整合代码现在项目已完成各种需求,可以实现时间设置、整点放音乐,显示时间、温度检测及报警功能.

项目遇到的主要困难

1.  对通信协议不是很了解  老是出现乱码.后来在csdn上看到一篇文章了解过后才解决问题https://blog.csdn.net/gs344937933/article/details/89930341?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161379580116780271526314%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161379580116780271526314&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-1-89930341.first_rank_v2_pc_rank_v29_10&utm_term=uart&spm=1018.2226.3001.4187

2.这次项目没有考虑资源的占用  消耗资源比较多.在整合时经常出现程序跑死了

而且还有很多需要优化的地方(在项目实现过程中有体现)

希望以后不断优化节省资源

一共用了1822个逻辑元件占比为79%(总数为2304)485个寄存器26个引脚

FpiL-VJSUyulQMtYHeak70_tkPPF

 

项目体会心得

这次项目深刻了解了fpga并行和以前的串行有不同的地方,开始有点不适应 但在经过不断的练习  找错觉得并行比串行不用考虑先后是很方便的,更容易找到错误,刚学习FPGA的时候有点困难,很不能理解FPGA"连起来"那种感觉但在不断地摸索中和在各个不同功能模块拼接起来的时候就能逐渐明白FPGA的原理,并能感觉FPGA有很大的优势.

附件下载
报警.zip
可下载的程序
团队介绍
成都信息工程大学通信工程学院
团队成员
赵新语
成都信息工程大学 通信工程学院
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号