任务目标
基于状态机实现4位共阴极数码管显示超声波模块读数。最近生产实习的FPGA培训课程内容,还是挺简单的。具体原理其他文章应该都烂大街了,重点是状态机的写法,还是很少博主写,没怎么看到,基本上都是时序机写的模块功能。
实现代码
4位共阴极数码模块代码seg.v:
`timescale 1ns/1ps
module seg
#(
parameter SYSCLK = 125_000_000,
parameter TIME = 1000,
parameter MODE = 0 // 0 gong yin ji; 1 gong yang ji
)(
input sysclk,
input rst_n,
input [4:0] set,//xian shi jin zhi:8/10/16
input [15:0] number,
output [3:0] DIG,
output [7:0] SEG
);
reg [3:0] DIG_t;
reg[7:0] SEG_t;
reg[31:0] cnt;
//-------------译码-----------//
function [7:0] seg_out;
input [3:0] number_in;
case(number_in)
4'd0: seg_out = 8'hc0;
4'd1: seg_out = 8'hf9;
4'd2: seg_out = 8'ha4;
4'd3: seg_out = 8'hb0;
4'd4: seg_out = 8'h99;
4'd5: seg_out = 8'h92;
4'd6: seg_out = 8'h82;
4'd7: seg_out = 8'hf8;
4'd8: seg_out = 8'h80;
4'd9: seg_out = 8'h90;
4'd10: seg_out = 8'h88;
4'd11: seg_out = 8'h83;
4'd12: seg_out = 8'hc6;
4'd13: seg_out = 8'ha1;
4'd14: seg_out = 8'h86;
4'd15: seg_out = 8'h8e;
default:seg_out = 8'hff;
endcase
endfunction
/* wei xuan qie huan */
always@(posedge sysclk)
if(!rst_n) begin
DIG_t <= 4'b0001;
cnt <= 32'd0;
end
else if(cnt>=TIME-1) begin
DIG_t <= {DIG_t[2:0],DIG_t[3]};
cnt <= 32'd0;
end
else begin
DIG_t <= DIG_t;
cnt <= cnt + 32'd1;
end
/*shu jv ti qv*/
reg[3:0] num1,num2,num3,num4;
always@(*)begin
num1 <= number%set; // ge wei
num2 <= (number/set)%set; //shi wei
num3 <= (number/(set*set))%set; //bai wei
num4 <= (number/(set*set*set))%set; // qian wei
end
/*duan xuan shu chu*/
always@(posedge sysclk)
if(!rst_n)
SEG_t <= 8'hff;
else
case(DIG_t)
4'b0001:SEG_t <= seg_out(num1);
4'b0010:SEG_t <= seg_out(num2);
4'b0100:SEG_t <= seg_out(num3);
4'b1000:SEG_t <= seg_out(num4);
default:SEG_t <= 8'hff;
endcase
assign SEG = (MODE)?SEG_t:~SEG_t;
assign DIG = (MODE)?DIG_t:~DIG_t;
endmodule
超声波模块代码trasonic.v:
`timescale 1ns / 1ps
module trasonic
#(
parameter SYSCLK = 125_000_000,
parameter TIME1 = 5000,//激励时间(大于等于10us)
parameter TIME2 = 200_000_00 //总周期 (大于等于80ms)
)
(
input sysclk ,
input rst_n ,
input Echo ,
output reg Trig ,
output reg [15:0] distence
);
localparam IDLE = 2'd0;
localparam TRIG = 2'd1;
localparam RECE = 2'd2;
reg [31:0] cnt;
reg [63:0] number;//距离寄存器
reg [1:0] cur_state,next_state;
reg [63:0]h_cnt;//记录高电平所在的次数
//____________________state1______________________//
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//______________________state2_____________________//
always@(*)begin
next_state = IDLE;
case(cur_state)
IDLE:begin
next_state = TRIG;
end
TRIG:begin
if(cnt >= TIME1 - 1)
next_state = RECE;
else
next_state = cur_state;
end
RECE:begin
if(cnt >= TIME2 - 1)
next_state = IDLE;
else
next_state = cur_state;
end
default:next_state = IDLE;
endcase
end
//________________________state3______________________//
always@(posedge sysclk)
if(!rst_n)begin
cnt <= 32'd0;
Trig <= 0;
h_cnt <= 64'd0;
end
else
case(cur_state)
IDLE:begin
cnt <= 32'd0;
Trig <= 0;
h_cnt <= 64'd0;
end
TRIG:begin
Trig <= 1;
cnt <= cnt + 32'd1;
h_cnt <= 64'd0;
end
RECE:begin
Trig <= 0;
cnt <= cnt + 32'd1;
if(Echo)
h_cnt <= h_cnt + 64'd1;
else
h_cnt <= h_cnt;
end
default:begin
cnt <= 32'd0;
Trig <= 0;
h_cnt <= 64'd0;
end
endcase
//_____________________数据处理_____________________//
localparam t = 1000_000_000;
localparam T = t/SYSCLK;//(8ns)
always@(*)
number = (cnt == (TIME2 - 1)) ? ((h_cnt*T)/58/1000):number;
//保证数据稳定传输
always@(*)
distence = number;
endmodule
顶层文件top.v:
module top(
input sysclk ,
input rst_n ,
input Echo ,
output Trig ,
output [7:0] SEG ,
output [3:0] DIG
);
wire [15:0] num;
seg
#(
.SYSCLK (125_000_000) ,
.TIME (1000) ,
.MODE (0)
)a(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.set (10) ,
.number (num) ,
.DIG (DIG) ,
.SEG (SEG)
);
trasonic
#(
.SYSCLK (125_000_000) ,
.TIME1 (5000 ) ,
.TIME2 (20_000_000 )
)b(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.Echo (Echo) ,
.Trig (Trig) ,
.distence(num)
);
endmodule
结语
这个需要的引脚挺多的——14个pin,在引脚配置的时候别重复了哦。不然生成二进制文件流的时候会报错。