项目描述及要实现的功能:
-
实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
-
实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
-
定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
-
PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
-
音频文件播放完毕,OLED开始更新时间信息和当前的温度信息
设计思路:
- oled显示时间和温度信息。
- 通过按键设置时间更新到时钟上,当时钟到14:00:00进行报警,发送当前时间和温度信息到上位机,并且oled停止刷新显示。
- 用上位机发送一份自制的文件到FPGA,FPGA收到文件后存在RAM中。
- 设置0.5秒读取一次RAM中数据到蜂鸣器中实现音乐的播放。
思维导图:
引脚信息:
资源报告:
RTL视图:
功能介绍:
sw1为oled显示模式切换开关,高电平为时间温度显示模式,低电平为时间设定模式。
sw4为复位开关,低电平复位。
key1为时间设定确认按键,按下即将设定时间更新为oled当前显示时间。
key3为数值加按键,按下将选中位选数值加1。
key4为位选切换按键,按下将切换数字位选。
代码具体实现:
- 顶层模块设计
连接各个模块。
module top(
input Clk,
input Rst_n,
input key_1_confirm,
input key_2_minus,
input key_3_add,
input key_4_shift,
input sw_1_oledmode,
input sw_2_buzzer,
input RXD,
inout one_wire,
output TXD,
output oled_csn,
output oled_rst,
output oled_clk,
output oled_dat,
output oled_dcn,
output Buzzer_pwm,
output led1_oled,
output led7_tx,
output led8_reset
);
reg oled_stopbit;
wire [3:0]sec_l;
wire [3:0]sec_h;
wire [3:0]min_l;
wire [3:0]min_h;
wire [3:0]hour_l;
wire [3:0]hour_h;
wire [3:0]timer_sec_l;
wire [3:0]timer_sec_h;
wire [3:0]timer_min_l;
wire [3:0]timer_min_h;
wire [3:0]timer_hour_l;
wire [3:0]timer_hour_h;
wire [3:0]temp_h;
wire [3:0]temp_l;
wire [3:0]temp_s;
wire [7:0]buzzer_data;
wire [7:0]rx_data,tx_data;
wire [7:0]wraddress,rdaddress;
wire Send_En;
wire Rx_Done,Tx_Done;
wire key1_state,key1_flag,key1_in;
wire key3_state,key3_flag,key3_in;
wire key4_state,key4_flag,key4_in;
wire buzz_en;
wire rden,wren;
wire timeout,ringout;
wire uart_tx_state;
assign led1_oled = sw_1_oledmode;
assign led7_tx = ~uart_tx_state;
assign led8_reset = Rst_n;
OLED_SPI OLED_SPI(
.clk(Clk), //12MHz系统时钟
.rst_n(Rst_n), //系统复位,低有效
.sw_1_oledmode(sw_1_oledmode),
.oled_stopbit(oled_stopbit),
.sec_l(sec_l),
.min_l(min_l),
.hour_l(hour_l),
.sec_h(sec_h),
.min_h(min_h),
.hour_h(hour_h),
.timer_sec_l(timer_sec_l),
.timer_sec_h(timer_sec_h),
.timer_min_l(timer_min_l),
.timer_min_h(timer_min_h),
.timer_hour_l(timer_hour_l),
.timer_hour_h(timer_hour_h),
.temp_h(temp_h),
.temp_l(temp_l),
.temp_s(temp_s),
.oled_csn(oled_csn), //OLCD液晶屏使能
.oled_rst(oled_rst), //OLCD液晶屏复位
.oled_dcn(oled_dcn), //OLCD数据指令控制
.oled_clk(oled_clk), //OLCD时钟信号
.oled_dat(oled_dat) //OLCD数据信号
);
temperature temperature(
.Clk(Clk),
.Rst_n(Rst_n),
.one_wire(one_wire),
.temp_h(temp_h),
.temp_l(temp_l),
.temp_s(temp_s)
);
clock clock(
.Clk(Clk),
.Rst_n(Rst_n),
.key1_in(key1_in),
.timer_sec_l(timer_sec_l),
.timer_sec_h(timer_sec_h),
.timer_min_l(timer_min_l),
.timer_min_h(timer_min_h),
.timer_hour_l(timer_hour_l),
.timer_hour_h(timer_hour_h),
.sec_l(sec_l),
.min_l(min_l),
.hour_l(hour_l),
.sec_h(sec_h),
.min_h(min_h),
.hour_h(hour_h),
.timeout(timeout)
);
Buzzer Buzzer(
.Clk(Clk),
.Rst_n(Rst_n),
.data_in(buzzer_data),
.buzz_en(buzz_en),
.pwm_out(Buzzer_pwm)
);
RAM RAM(
.aclr(1'b0),
.clock(Clk),
.data(rx_data),
.rdaddress(rdaddress),
.rden(rden),
.wraddress(wraddress),
.wren(wren),
.q(buzzer_data)
);
uart_rxd uart_rxd(
.Clk(Clk), //模块时钟12M
.Rst_n(Rst_n), //模块复位
.baud_set(3'd0), //波特率设置
.Rs232_Rx(RXD), //RXD
.data_byte(rx_data), //并行数据输出
.Rx_Done(Rx_Done) //一次数据接收完成标志
);
uart_txd uart_txd(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(3'd0),
.data_byte(tx_data),
.Send_En(Send_En),
.Rs232_Tx(TXD),//TXD
.Tx_Done(Tx_Done),//标志数据传输完毕
.uart_state(uart_tx_state)//标志数据正在发送
);
rx_buzz_ctrl rx_buzz_ctrl(
.Clk(Clk),
.Rst_n(Rst_n),
.Rx_Done(Rx_Done),
.sw_2_buzzer(sw_2_buzzer),//高使能
.wren(wren),
.rden(rden),
.wraddress(wraddress),
.rdaddress(rdaddress),
.buzz_en(buzz_en),
.ringout(ringout)
);
timeout_tx_ctrl timeout_tx_ctrl(
.Clk(Clk),
.Rst_n(Rst_n),
.timeout(timeout),
.Tx_Done(Tx_Done),
.sec_l(sec_l),
.sec_h(sec_h),
.min_l(min_l),
.min_h(min_h),
.hour_l(hour_l),
.hour_h(hour_h),
.temp_h(temp_h),
.temp_l(temp_l),
.temp_s(temp_s),
.Send_En(Send_En),
.tx_data(tx_data)
);
timer timer(
.Clk(Clk),
.Rst_n(Rst_n),
// .key2_in(key2_in),//minus
.key3_in(key3_in),//add
.key4_in(key4_in),
.timer_sec_l(timer_sec_l),
.timer_sec_h(timer_sec_h),
.timer_min_l(timer_min_l),
.timer_min_h(timer_min_h),
.timer_hour_l(timer_hour_l),
.timer_hour_h(timer_hour_h)
);
key key1(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_1_confirm),
.key_flag(key1_flag),
.key_state(key1_state)
);
key key3(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_3_add),
.key_flag(key3_flag),
.key_state(key3_state)
);
key key4(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_4_shift),
.key_flag(key4_flag),
.key_state(key4_state)
);
assign key1_in = key1_flag && !key1_state;
assign key3_in = key3_flag && !key3_state;
assign key4_in = key4_flag && !key4_state;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n)
oled_stopbit <= 1'b0;
else if (timeout)
oled_stopbit <= 1'b1;
else if (ringout)
oled_stopbit <= 1'b0;
else
oled_stopbit <= oled_stopbit;
endmodule
- 时钟模块
产生时分秒的24小时时钟,当按键key1按下时可更新设定的时间,当时间到达14:00:00时产生timeout信号。
module clock(
input Clk,
input Rst_n,
input key1_in,
input [3:0]timer_sec_l,
input [3:0]timer_sec_h,
input [3:0]timer_min_l,
input [3:0]timer_min_h,
input [3:0]timer_hour_l,
input [3:0]timer_hour_h,
output reg[3:0]sec_l,
output reg[3:0]sec_h,
output reg[3:0]min_l,
output reg[3:0]min_h,
output reg[3:0]hour_l,
output reg[3:0]hour_h,
output reg timeout
);
reg Clk_1s;
reg [23:0]cnt_1s;
reg key1;
// assign timeout = (hour_h == 4'd1 && hour_l == 4'd4 && min_h == 4'd0 && min_l == 4'd0 && sec_h == 4'd0 && sec_l == 4'd0);
always @(posedge Clk or negedge Rst_n)//达到报警时间
if (!Rst_n)
timeout <= 1'b0;
else if (hour_h == 4'd1 && hour_l == 4'd4 && min_h == 4'd0 && min_l == 4'd0 && sec_h == 4'd0 && sec_l == 4'd0)
timeout <= 1'b1;
else
timeout <= 1'b0;
always @(posedge Clk, negedge Rst_n)//1HZ时钟
if (!Rst_n)begin
Clk_1s <= 1'd0;
cnt_1s <= 24'd0;
end
else if (cnt_1s == 24'd12_000_000 - 1) begin
Clk_1s <= 1'd1;
cnt_1s <= 24'd0;
end
else begin
Clk_1s <= 1'd0;
cnt_1s <= cnt_1s + 1'b1;
end
always @(posedge key1_in or posedge Clk_1s or negedge Rst_n)//更新设定时间
if (!Rst_n)
key1 <= 1'b0;
else if (key1_in)
key1 <= 1'b1;
else
key1 <= 1'b0;
always @(posedge Clk_1s, negedge Rst_n)//秒
if (!Rst_n)
sec_l <= 4'd7;
else if (key1)
sec_l <= timer_sec_l;
else if (sec_l == 4'd9)
sec_l <= 4'd0;
else
sec_l <= sec_l + 1'b1;
always @(posedge Clk_1s, negedge Rst_n)
if (!Rst_n)
sec_h <= 4'd2;
else if (key1)
sec_h <= timer_sec_h;
else if (sec_h == 4'd5 && sec_l == 4'd9)
sec_h <= 4'd0;
else if (sec_l == 4'd9)
sec_h <= sec_h + 1'b1;
else
sec_h <= sec_h;
always @(posedge Clk_1s, negedge Rst_n)//分
if (!Rst_n)
min_l <= 4'd5;
else if (key1)
min_l <= timer_min_l;
else if (sec_h == 4'd5 && sec_l == 4'd9)
if (min_l == 4'd9)
min_l <= 4'd0;
else
min_l <= min_l + 1'b1;
else
min_l <= min_l;
always @(posedge Clk_1s, negedge Rst_n)
if (!Rst_n)
min_h <= 4'd3;
else if (key1)
min_h <= timer_min_h;
else if (min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
min_h <= 4'd0;
else if (min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
min_h <= min_h + 1'b1;
else
min_h <= min_h;
always @(posedge Clk_1s, negedge Rst_n)//时
if (!Rst_n)
hour_l <= 4'd4;
else if (key1)
hour_l <= timer_hour_l;
else if (min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
if (hour_l == 4'd9 || (hour_h == 4'd2 && hour_l == 4'd3 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9))
hour_l <= 4'd0;
else
hour_l <= hour_l + 1'b1;
else
hour_l <= hour_l;
always @(posedge Clk_1s, negedge Rst_n)
if (!Rst_n)
hour_h <= 4'd1;
else if (key1)
hour_h <= timer_hour_h;
else if (hour_h == 4'd2 && hour_l == 4'd3 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
hour_h <= 4'd0;
else if (hour_l == 4'd9 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
hour_h <= hour_h + 1'b1;
else
hour_h <= hour_h;
endmodule
- 按键消抖模块
采用状态机对独立按键进行消抖。
module key(
Clk,
Rst_n,
key_in,
key_flag,
key_state
);
input Clk;
input Rst_n;
input key_in;
output reg key_flag;//按键是否处于触发沿
output reg key_state;//按键当前电平
reg key_in_s1,key_in_s2;
always@(posedge Clk, negedge Rst_n)//对外部输入信号进行处理
if(!Rst_n)begin
key_in_s1 <= 1'b0;
key_in_s2 <= 1'b0;
end
else begin
key_in_s1 <= key_in;
key_in_s2 <= key_in_s1;
end
reg key_tmp1,key_tmp2;
wire pedge,nedge;
always@(posedge Clk,negedge Rst_n)//D触发器存储两个相邻时钟上升沿时的外部输入信号
if(!Rst_n)begin
key_tmp1 <= 1'b0;
key_tmp2 <= 1'b0;
end
else begin
key_tmp1 <= key_in_s2;
key_tmp2 <= key_tmp1;
end
assign pedge = (!key_tmp1) & key_tmp2;//跳变沿
assign nedge = key_tmp1 & (!key_tmp2);
reg[19:0]cnt;
reg en_cnt;
reg cnt_full;
always@(posedge Clk,negedge Rst_n)//计数器
if (!Rst_n)
cnt <= 20'b0;
else
if (en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'b0;
always@(posedge Clk,negedge Rst_n)
if (!Rst_n)
cnt_full <= 1'b0;
else if (cnt == 20'd999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
localparam
IDEL = 4'b0001,
FILTER0 = 4'b0010,
DOWN = 4'b0100,
FILTER1 = 4'b1000;
reg [3:0]state;
always @(posedge Clk,negedge Rst_n)//按键状态机
if (!Rst_n)begin
state = IDEL;
key_flag <= 1'b0;//防止发生复位时其他参数未复位
key_state <= 1'b1;
en_cnt = 1'b1;
end
else begin
case(state)
IDEL: begin
key_flag <= 1'b0;
if (nedge)begin
state <= FILTER0;
en_cnt <= 1'b1;
end
else
state <= IDEL;
end
FILTER0: begin
if (cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
en_cnt <=1'b0;
state <= DOWN;
end
else if (pedge)begin
state <= IDEL;
en_cnt <= 1'b0;
end
else
state <= FILTER0;
end
DOWN: begin
key_flag = 1'b0;
if (pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
state = DOWN;
end
FILTER1: begin
if (cnt_full) begin
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
en_cnt <= 1'b0;
end
else if(nedge)begin
en_cnt <= 1'b0;
state = DOWN;
end
else
state <= FILTER1;
end
default: begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag = 1'b0;
key_state = 1'b1;
end
endcase
end
endmodule
- 串口接收模块
接收来自上位机通过串口发送的数据,并将串行数据转换为并行数据。
module uart_rxd(
Clk, //模块时钟12M
Rst_n, //模块复位
baud_set, //波特率设置
Rs232_Rx, //RS232数据输入
data_byte, //并行数据输出
Rx_Done //一次数据接收完成标志
);
input Clk;
input Rst_n;
input [2:0]baud_set;
input Rs232_Rx;
output reg [7:0]data_byte;
output reg Rx_Done;//数据接收完成
wire nedege;//启示信号下降沿
reg [2:0]START_BIT, STOP_BIT;//数据接收起始标志位
reg uart_state;//数据接收状态
//串行同步输入处理,将异步输入信号转化为同步输入信号
reg s0_Rs232_Rx,s1_Rs232_Rx; //同步寄存器
always@(posedge Clk or negedge Rst_n)//同步寄存器,消除亚稳态
if(!Rst_n)begin
s0_Rs232_Rx <= 1'b0;
s1_Rs232_Rx <= 1'b0;
end
else begin
s0_Rs232_Rx <= Rs232_Rx;
s1_Rs232_Rx <= s0_Rs232_Rx;
end
reg tmp0_Rs232_Rx,tmp1_Rs232_Rx; //数据寄存器
always@(posedge Clk or negedge Rst_n)//数据寄存器
if(!Rst_n)begin
tmp0_Rs232_Rx <= 1'b0;
tmp1_Rs232_Rx <= 1'b0;
end
else begin
tmp0_Rs232_Rx <= s1_Rs232_Rx;
tmp1_Rs232_Rx <= tmp0_Rs232_Rx;
end
assign nedege = !tmp0_Rs232_Rx && tmp1_Rs232_Rx;//标志接收到起始下降沿信号
//采样时钟生成模块
//过采样方式对接收到数据进行采样,采样频率为波特率的16倍
//分频计数最大值 = (系统时钟频率 / (波特率 * 16))- 1
reg [15:0]bps_DR;//分频计数最大值
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd77;
else begin
case(baud_set)
0:bps_DR <= 16'd77;//9600
1:bps_DR <= 16'd38;//19200
// 2:bps_DR <= 16'd18;//38400
// 3:bps_DR <= 16'd13;//57600
// 4:bps_DR <= 16'd5;//115200
default:bps_DR <= 16'd77;
endcase
end
reg [15:0]div_cnt;//采样时钟分频计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
reg bps_clk;//采样时钟频率
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
reg [7:0]bps_cnt;//采样时钟计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))//StART_BIT大于2说明采集到干扰信号
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
always@(posedge Clk or negedge Rst_n)//标志接收完成
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
//采样数据接收模块
reg [2:0]r_data_byte[7:0];
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0: begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
default:
begin
START_BIT <= START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
//数据状态判定模块
always@(posedge Clk or negedge Rst_n)//累加结果最高位为1表示接收结果为1,为0表示接收结果为0
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
else begin
data_byte[0] <= data_byte[0];
data_byte[1] <= data_byte[1];
data_byte[2] <= data_byte[2];
data_byte[3] <= data_byte[3];
data_byte[4] <= data_byte[4];
data_byte[5] <= data_byte[5];
data_byte[6] <= data_byte[6];
data_byte[7] <= data_byte[7];
end
always@(posedge Clk or negedge Rst_n)//数据接收状态
if(!Rst_n)
uart_state <= 1'b0;
else if(nedege)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
endmodule
- 接收数据蜂鸣器响应控制模块
控制RAM写入来自串口接收模块的数据,并用0.5s时钟控制RAM输出数据给蜂鸣器。
module rx_buzz_ctrl(
input Clk,
input Rst_n,
input Rx_Done,
input sw_2_buzzer,//高使能
output wren,
output reg rden,
output reg[7:0]wraddress,
output reg[7:0]rdaddress,
output buzz_en,
output reg ringout
);
reg [23:0]cnt;
reg Clk_0_5s;
assign wren = Rx_Done;
assign buzz_en = rden;
always @(posedge Clk, negedge Rst_n)//读使能
if (!Rst_n)
rden <= 1'b0;
else if (!ringout) begin
rden <= 1'b1;
end
else
rden <= 1'b0;
always @(posedge Clk, negedge Rst_n)//写RAM
if (!Rst_n)
wraddress <= 8'd0;
else if (wren)
wraddress <= wraddress + 1'b1;
else
wraddress <= wraddress;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n)
cnt <= 24'd0;
else if (cnt == 6_000_000 - 1) begin
cnt <= 24'd0;
Clk_0_5s <= 1'b1;
end
else begin
cnt <= cnt + 1'b1;
Clk_0_5s <= 1'b0;
end
always @(posedge Clk_0_5s, negedge Rst_n)//读RAM
if (!Rst_n)
rdaddress <= 8'd0;
else if (rden)
if (rdaddress == wraddress)
rdaddress <= 8'd0;
else
rdaddress <= rdaddress + 1'b1;
else
rdaddress <= 8'd0;
always @(posedge Clk_0_5s, negedge Rst_n)//读RAM
if (!Rst_n)
ringout <= 1'b0;
else if (rden)
if (rdaddress == wraddress && rdaddress != 8'd0)
ringout <= 1'b1;
else
ringout <= ringout;
else
ringout <= ringout;
endmodule
- 串口发送模块
将并行数据转化为串行数据再发送给上位机。
module uart_txd(
Clk,
Rst_n,
baud_set,
data_byte,
Send_En,
Rs232_Tx,//TXD
Tx_Done,//标志数据传输完毕
uart_state//标志数据正在发送
);
input Clk;
input Rst_n;
input [2:0]baud_set;
input [7:0]data_byte;
input Send_En;
output reg Rs232_Tx;
output reg Tx_Done;
output reg uart_state;
//波特率时钟生成模块
//分频计数最大值 == (系统时钟频率 / 波特率) - 1
reg [15:0]bps_DR;//波特率分频计数最大值
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
bps_DR <= 16'd1249;
else begin
case(baud_set)
0:bps_DR <= 16'd1249; //9600bps
1:bps_DR <= 16'd624; //19200bps
2:bps_DR <= 16'd311; //38400bps
3:bps_DR <= 16'd207; //57600bps
4:bps_DR <= 16'd103; //115200bps
default:bps_DR <= 16'd1249;
endcase
end
reg [15:0]div_cnt;//波特率分频计数器
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
div_cnt <= 16'd0;
else if (uart_state) begin
if (div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
reg bps_clk;//波特率时钟
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
bps_clk <= 1'b0;
else if (div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//数据输出模块
reg [3:0]bps_cnt;//波特率时钟计数器
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
bps_cnt <= 4'd0;
else if (bps_cnt == 4'd11)
bps_cnt <= 4'd0;
else if (bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
always @(posedge Clk or negedge Rst_n)//标志数据传输完毕
if (!Rst_n)
Tx_Done <= 1'b0;
else if (bps_cnt == 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
always @(posedge Clk or negedge Rst_n)//标志数据正在发送
if (!Rst_n)
uart_state <= 1'b0;
else if (Send_En)
uart_state <= 1'b1;
else if (bps_cnt == 4'd11)
uart_state <= 1'b0;
else
uart_state <= uart_state;
reg [7:0]r_data_byte;//数据发送寄存器
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
r_data_byte <= 8'd0;
else if (Send_En)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
//数据传输状态控制模块
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rs232_Tx <= 1'b1;
else begin
case(bps_cnt)
0:Rs232_Tx <= 1'b1;
1:Rs232_Tx <= START_BIT;
2:Rs232_Tx <= r_data_byte[0];
3:Rs232_Tx <= r_data_byte[1];
4:Rs232_Tx <= r_data_byte[2];
5:Rs232_Tx <= r_data_byte[3];
6:Rs232_Tx <= r_data_byte[4];
7:Rs232_Tx <= r_data_byte[5];
8:Rs232_Tx <= r_data_byte[6];
9:Rs232_Tx <= r_data_byte[7];
10:Rs232_Tx <= STOP_BIT;
default:Rs232_Tx <= 1'b1;
endcase
end
endmodule
- 定时报警发送温度数据控制模块
接收到timeout信号后控制串口发送模块将温度转换模块数据发送给上位机。
module timeout_tx_ctrl(
input Clk,
input Rst_n,
input timeout,
input Tx_Done,
input [3:0]sec_l,
input [3:0]sec_h,
input [3:0]min_l,
input [3:0]min_h,
input [3:0]hour_l,
input [3:0]hour_h,
input [3:0]temp_h,
input [3:0]temp_l,
input [3:0]temp_s,
output reg Send_En,
output reg[7:0]tx_data
);
reg timeout_flag;
reg send_out;
reg [5:0]cnt;//42
initial send_out = 1'b0;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n)
timeout_flag <= 1'b0;
else if (timeout)
timeout_flag <= 1'b1;
else if (send_out)
timeout_flag <= 1'b0;
else
timeout_flag <= timeout_flag;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n)
cnt <= 6'd0;
else if (cnt == 6'd45)
cnt <= 6'd0;
else if (Tx_Done)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n)
Send_En <= 1'b0;
else if (timeout_flag && !send_out)
Send_En <= 1'b1;
else
Send_En <= 1'b0;
always @(posedge Clk, negedge Rst_n)
if (!Rst_n) begin
tx_data <= 8'h00;
send_out <= 1'b0;
end
else if (timeout_flag)
case (cnt)
0: tx_data <= 8'hB5;//当
1: tx_data <= 8'hB1;
2: tx_data <= 8'hC7;//前
3: tx_data <= 8'hB0;
4: tx_data <= 8'hCA;//时
5: tx_data <= 8'hB1;
6: tx_data <= 8'hBC;//间
7: tx_data <= 8'hE4;
8: tx_data <= 8'hCE;//为
9: tx_data <= 8'hAA;
10: tx_data <= 8'hA3;//:
11: tx_data <= 8'hBA;
12: tx_data <= 8'h20;//
13: tx_data <= 8'h30 + hour_h;
14: tx_data <= 8'h30 + hour_l;
15: tx_data <= 8'hA3;//:
16: tx_data <= 8'hBA;
17: tx_data <= 8'h30 + min_h;
18: tx_data <= 8'h30 + min_l;
19: tx_data <= 8'hA3;//:
20: tx_data <= 8'hBA;
21: tx_data <= 8'h30 + sec_h;
22: tx_data <= 8'h30 + sec_l;
23: tx_data <= 8'h0D;//\r
24: tx_data <= 8'h0A;//\n
26: tx_data <= 8'hB5;//当
27: tx_data <= 8'hB1;
28: tx_data <= 8'hC7;//前
29: tx_data <= 8'hB0;
30: tx_data <= 8'hCE;//温
31: tx_data <= 8'hC2;
32: tx_data <= 8'hB6;//度
33: tx_data <= 8'hC8;
34: tx_data <= 8'hCE;//为
35: tx_data <= 8'hAA;
36: tx_data <= 8'hA3;//:
37: tx_data <= 8'hBA;
38: tx_data <= 8'h0D;//
39: tx_data <= 8'h30 + temp_h;
40: tx_data <= 8'h30 + temp_l;
41: tx_data <= 8'h2E;//.
42: tx_data <= 8'h30 + temp_s;
43: tx_data <= 8'h0D;//\r
44: tx_data <= 8'h0A;//\n
45: begin tx_data <= 8'h00; send_out <= 1'b1;end
default: tx_data <= 8'h00;
endcase
else
tx_data <= 8'h00;
endmodule
遇到的困难及解决方法:
- 串口发送和接收模块的编写。
通过网上浏览大量资料写出串口发送和接收模块。
- 编程过程中遇到的时序问题。
通过网上寻找资料解决。
- 写完代码下载到板子上发现有许多BUG。
通过Modelsim仿真和仔细查找代码的方法解决一些列BUG。
意见与建议:
- 希望下一代产品的硬件资源方面能提升,逻辑单元较少,以至于代码写到一半逻辑单元就用光了,只好优化代码,体会到为什么嵌入式工程师编程需考虑硬件的资源分配。
- 加入更多的各种接口。
心得体会:
- 第一次接触FPGA,知道了硬件编程和软件编程有很大的不同。用Verilog编程不能完全用C编程的思维去思考,而是需要用硬件的思维去思考,头脑里先要对解决的问题有电路的抽象行为级建模,再将电路模型映射到Verilog代码上。还需要考虑一系列的时序问题,也学会了Moelsim仿真的使用。
- 通过这个项目让我对所学的知识有了更深的认知,同时也在学习中收获了快乐,提升了对电子学习的兴趣。