基于FPGA的超声波测距

文章目录

  • 一、项目框架
  • 二、超声波测距模块
  • 三、串口模块
    • 1.串口发送模块
    • 2.串口发送控制模块
  • 四、蜂鸣器模块
  • 五、vga显示模块
    • 1.vga协议驱动代码
    • 2.vga数据控制模块
  • 六、数码管
    • 1.数码管段选控制
    • 2.数码管位选
  • 七、顶层文件
  • 八、源代码

一、项目框架

distance超声波测距模块负责数据的采集,vga、uart、beep、数码管根据采集到的数据分别进行vga的屏幕打点、串口输出到上位机、蜂鸣器根据数据大小进行鸣叫以及数码管显示采集到的数据。
基于FPGA的超声波测距_第1张图片

RTL视图

基于FPGA的超声波测距_第2张图片

二、超声波测距模块

基于FPGA的超声波测距_第3张图片
基于FPGA的超声波测距_第4张图片
代码

module distance_drive (
    input			wire						clk,
    input			wire						clk_1,
    input			wire						rst_n,

    input			wire						echo,
    output			reg						    trig,
    output			wire						data_out_vld,
    output			wire		[ 23:0 ]		distance_data
);
localparam	MAX_DISTANCE = 117647; //最大距离 4m

parameter	s_idle = 0;//空闲状态
parameter	s_send = 1;//发送触发信号
parameter	s_wait = 2;//等待内部发送脉冲
parameter	s_accept = 3;//检测回响信号
parameter	s_accept_wait = 4;//延时等待


reg								echo_r0			;
reg								echo_r1			;
reg			[ 2:0 ]			    s_current		;
reg			[ 2:0 ]			    s_next			;
reg			[ 22 :0 ]		    cnt				;
reg			[ 22:0 ]			cnt_wait		;
reg			[ 22:0 ]		    cnt_max			;
reg			[ 16:0 ]			cnt_distance	;
// reg			[ 25:0 ]			cnt_distance_r1			;
// reg			[ 19:0 ]			cnt_distance_r2			;

wire							accept_start_sig			;
wire							accept_stop_sig			;
wire							idle_sig			;
wire							send_sig			;
// wire							wait_sig			;
wire							flag_clear_cnt			;
wire							flag_clear_cnt_wait			;
reg			[ 19:0 ]			distance_data_r			;
wire			[ 23:0 ]			distance_data_r1			;

assign idle_sig = s_current == s_idle;
assign send_sig = s_current == s_send && flag_clear_cnt;
// assign wait_sig = s_current == s_wait && flag_clear_cnt_wait;
assign accept_wait_sig = s_current == s_accept_wait &&  flag_clear_cnt_wait;
assign accept_start_sig = s_current == s_wait && echo_r0 && ~echo_r1;
assign accept_stop_sig = s_current == s_accept && (~echo_r0 && echo_r1);


// always @(posedge clk or negedge rst_n) begin
//     if(!rst_n) begin
//         cnt_distance_r1 <= 0;
//         // cnt_distance_r2 <= 0;
//     end
//     else begin
//         cnt_distance_r1 <= cnt_distance * 340 / 100;
//         // cnt_distance_r2 <= cnt_distance_r1 >> 4;
//     end
// end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        echo_r0 <= 0;
        echo_r1 <= 0;
    end
    else begin
        echo_r0 <= echo;
        echo_r1 <= echo_r0;
    end
end
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        s_current <= s_idle;    
    end
    else begin
        s_current <= s_next;
    end
end

always @(*) begin
    case (s_current)
        s_idle : begin
            if(idle_sig) begin
                s_next = s_send;
            end
            else begin
                s_next = s_idle;
            end
        end
        s_send : begin
            if(send_sig) begin
                s_next = s_wait;
            end
            else begin
                s_next = s_send;
            end
        end
        s_wait : begin
            if(accept_start_sig) begin
                s_next = s_accept;
            end
            else begin
                s_next = s_wait;
            end
        end
        s_accept : begin
            if(accept_stop_sig) begin
                s_next = s_accept_wait;
            end
            else begin
                s_next = s_accept;
            end
        end
        s_accept_wait : begin
            if(accept_wait_sig) begin
                s_next <= s_idle;
            end
            else begin
                s_next <= s_accept_wait;
            end
        end
        default: s_next = s_idle;
    endcase
end

//距离
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        distance_data_r <= 0;
    end
    else if(accept_stop_sig) begin
        distance_data_r <= cnt_distance * 340 / 200;
    end
end

//转BCD码
assign     distance_data_r1[3:0]   = distance_data_r % 10;
assign     distance_data_r1[7:4]   = distance_data_r / 10 % 10;
assign     distance_data_r1[11:8]  = distance_data_r / 100 % 10;
assign     distance_data_r1[15:12] = distance_data_r / 1000 % 10;
assign     distance_data_r1[19:16] = distance_data_r / 10000 % 10;
assign     distance_data_r1[23:20] = distance_data_r / 100000 % 10;

assign data_out_vld = accept_wait_sig;
assign distance_data = distance_data_r1;

//回响信号计数器
always @(posedge clk_1 or negedge rst_n) begin
    if(!rst_n) begin
        cnt_distance <= 0;
    end
    else if(accept_start_sig) begin
        cnt_distance <= 0;
    end
    else if(s_current == s_accept) begin
        cnt_distance <= cnt_distance + 1;
    end
    else begin
        cnt_distance <= 0;
    end
end

//发送触发信号
always @(posedge clk_1 or negedge rst_n) begin
    case (s_current)
        s_idle : begin
            trig <= 0;
        end
        s_send : begin
            trig <= 1;
        end
        s_wait : begin
            trig <= 0;
        end
        s_accept : begin
            trig <= 0;
        end
        s_accept_wait : begin
            trig <= 0;
        end
        default: begin
            trig <= 0;
        end
    endcase
end
//等待发送玩脉冲
always @( posedge clk_1 or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt <= 0;
    end
    else if ( s_current == s_send ) begin
        if ( flag_clear_cnt == 9 ) begin
            cnt <= 0;
        end
        else begin
            cnt <= cnt + 1;
        end
    end
    else begin
        cnt <= 0;
    end
end
assign flag_clear_cnt = cnt == 9;

//延时计数器
always @( posedge clk_1 or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_wait <= 0;
    end
    else if ( s_current == s_accept_wait ) begin
        if ( flag_clear_cnt_wait ) begin
            cnt_wait <= 0;
        end
        else begin
            cnt_wait <= cnt_wait + 1;
        end
    end
    else begin
        cnt_wait <= 0;
    end
end
assign flag_clear_cnt_wait = cnt_wait == 250_000;
endmodule //distance

三、串口模块

基于FPGA的超声波测距_第5张图片

1.串口发送模块

module uart_tx(input			wire						clk,
               input			wire						rst_n,
               input			wire						tx_enable, // 发送使能
               input			wire		[ 07:0 ]		data_in, // 需要发送的数据
               input			wire		[ 19:0 ]		tx_bps, // 发送的波特率
               output			wire						data, // 发送的数据
               output			wire						tx_done);
    
    localparam MAX_BIT = 10;
    
    reg			[ 09:0 ]			data_r			; // 数据寄存器
    reg			[ 12:0 ]			cnt_bps			; // 波特率计数器
    reg			[ 03:0 ]			cnt_bit			; // 数据位计数器
    
    wire		[ 12:0 ]			max_bps			; // 波特率对应频率

    wire							flag_clear_cnt_bps			; // 计数器归零
    wire							flag_add_cnt_bit			; // 计数器+1
    wire							flag_clear_cnt_bit			; 
    reg								flag_send_data			    ; //发送数据标志
    
    //输入数据寄存
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            data_r <= 10'b0;
        end
        else if(tx_enable) begin
            data_r <={1'b1, data_in, 1'b0};
        end

    end
    
    // 波特率计数器
    always @( posedge clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            cnt_bps <= 0;
        end
        else if ( flag_send_data ) begin
            if ( flag_clear_cnt_bps ) begin
                cnt_bps <= 0;
            end
            else begin
                cnt_bps <= cnt_bps + 1;
            end
        end
        else begin
            cnt_bps <= 0;
        end
        
    end

    assign flag_clear_cnt_bps  = cnt_bps >= max_bps -1;
    assign max_bps             = 50_000_000 / tx_bps;
    
    // 数据位计数器
    always @( posedge clk or negedge rst_n ) begin
        if ( !rst_n ) begin
            cnt_bit <= 0;
        end
        else if ( flag_send_data ) begin
            if ( flag_clear_cnt_bit ) begin
                cnt_bit <= 0;
            end
            else if ( flag_add_cnt_bit )begin
                cnt_bit <= cnt_bit + 1;
            end
            else begin
                cnt_bit <= cnt_bit;
            end
        end
        else begin
            cnt_bit <= 0;
        end
    end

    assign flag_add_cnt_bit   = flag_clear_cnt_bps;
    assign flag_clear_cnt_bit = cnt_bit >= MAX_BIT - 1 && flag_add_cnt_bit ;


    //发送数据标志
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            flag_send_data <= 0;
        end
        else if(tx_enable) begin
            flag_send_data <= 1;
        end
        else if(flag_clear_cnt_bit) begin
            flag_send_data <= 0;
        end
        else begin
            flag_send_data <= flag_send_data;
        end
    end
    //发送数据
    assign data = flag_send_data ? data_r[cnt_bit]:1;
    assign tx_done = ~flag_send_data  ;

endmodule

2.串口发送控制模块

串口控制模块把接受到的24位BCD码转换成ASSIC码,并且清除高位零位和添加单位和小数点。把处理好的数据加入FIFO中,再通过串口发送模块进行发送。

module uart_drive (
    input			wire						clk,
    input			wire						rst_n,
    input			wire		[ 23:0 ]		distance_data,
    input			wire						data_vld,
    output			wire						rx_data,
    output			wire						tx_data
);
reg			[ 23:0 ]			distance_data_r			;
reg			[ 7:0 ]				data		;
reg			[ 3:0 ]				cnt_byte		;
reg								send_flag			;
wire		[ 7:0 ]			    distance			;
wire							rdreq			;
wire							wrreq			;
wire							empty			;
wire							full			;
wire		[ 7:0 ]			    data_in			;

reg								flag			;
//串口
uart_tx u_uart_tx(
    .clk       ( clk       ),
    .rst_n     ( rst_n     ),
    .tx_enable ( rdreq      ),
    .data_in   ( data_in   ),
    .tx_bps    ( 115200    ),
    .data      ( tx_data      ),
    .tx_done   ( tx_done   )
);

assign rdreq = tx_done && ~empty;
assign wrreq = ~full && send_flag && (cnt_byte > 0) && flag;
assign distance = data;
tx_fifo	tx_fifo_inst (
	.aclr ( ~rst_n ),
	.clock ( clk ),
	.data ( distance ),
	.rdreq ( rdreq ),
	.wrreq ( wrreq ),
	.empty ( empty ),
	.full ( full ),
	.q ( data_in ),
	.usedw ( usedw_sig )
	);


always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        send_flag <= 0;
    end
    else if(cnt_byte == 9) begin
        send_flag <= 0;
    end
    else if(data_vld) begin
        send_flag <= 1;
    end

end
//数据计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_byte <=0;
    end
    else if(cnt_byte == 9) begin
        cnt_byte <= 0;
    end
    else if(send_flag) begin
        cnt_byte <= cnt_byte + 1;
    end
end
//寄存数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        distance_data_r <=0;
    end
    else if(data_vld) begin
        distance_data_r <= distance_data;
    end
end

//去除前面的不必要的0
always @(*) begin
    if(!rst_n) begin
        flag = 0;
    end
    else if(!send_flag) begin
        flag <= 0;
    end
    else if(cnt_byte > 3 || data> 48) begin
        flag = 1;
    end
    else begin
        flag <= flag;
    end
end
//发送距离
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        data <=0;
    end
    else if(send_flag) begin
        case (cnt_byte)
            0 : data <= distance_data_r[23:20] + 48;
            1 : data <= distance_data_r[19:19] + 48;
            2 : data <= distance_data_r[15:12] + 48;
            3 : data <= distance_data_r[11:8 ] + 48;
            4 : data <= 46; // .
            5 : data <= distance_data_r[7 : 4] + 48;
            6 : data <= distance_data_r[3 : 0] + 48;
            7 : data <= 99 ; //c
            8 : data <= 109; //m
            default: data <=0;
        endcase
        // data <= distance_data_r[(4 * (6-cnt_byte) -1) -:4] + 48;
    end
end

endmodule //uart_drive

四、蜂鸣器模块

蜂鸣器模块把接受到的数据去掉低两位,也就是精度变成厘米级别。当处理完后的数据在MAX_DISTANCE和MIN_DISTANCE之间,则会根据数据的大小调整蜂鸣器鸣叫间隔,使得蜂鸣器的鸣叫频率随着距离的减少越来越高,当数据小于MIN_DISTANCE时则会一直处在鸣叫。

module beep_dirve (
    input			wire						clk,
    input			wire						rst_n,
    input			wire						beep_vld,
    input			wire						data_vld,
    input			wire		[ 23:0 ]		distance_data,
    output			reg						    beep
);

parameter	MAX_DISTANCE = 20;
parameter	MIN_DISTANCE = 10;
parameter	MAX_TIME = 50_000_000;
reg			[ 27:0 ]			cnt			        ;
wire		[ 27:0 ]			delay			    ;
wire		[ 19:0 ]			distance			;
reg			[ 23:0 ]			distance_data_r			;
// wire			[ 19:0 ]			distance_r			;

//寄存数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        distance_data_r <= 0;
    end
    else if(data_vld) begin
        distance_data_r <= distance_data;
    end
end
// 根据距离设置翻转频率
assign distance = distance_data_r[11:8] + distance_data_r[15:12] * 10 + distance_data_r[19:16] * 100 + distance_data_r[23:20] *1000;
assign delay =  ((distance ) + 1) * 200_000;

// // led
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt <= 0;
    end
    else if ( cnt >= delay  ) begin
        cnt <= 0;
    end
    else begin
        cnt <= cnt + 1; 
    end 
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        beep <= 1;
    end
    else if(~beep_vld) begin
        beep <= 1;
    end
    else if(distance <= MAX_DISTANCE && distance >= MIN_DISTANCE && cnt == 1 && beep_vld) begin
        beep <= ~ beep;
    end
    else if(distance < MIN_DISTANCE && beep_vld) begin
        beep <= 0;
    end
    else if(distance > MAX_DISTANCE) begin
        beep <= 1;
    end
    else begin
        beep <= beep;
    end
end

endmodule //beep_dirve

五、vga显示模块

基于FPGA的超声波测距_第6张图片

1.vga协议驱动代码

    module vga_dirve (input			wire						clk,            //系统时钟
                    input			wire						rst_n,          //复位
                    input			wire		[ 15:0 ]		rgb_data,       //16位RGB对应值
                    output			wire						vga_clk,    //vga时钟 25M
                    output			reg							h_sync,     //行同步信号
                    output			reg							v_sync,     //场同步信号
                    output			reg		[ 11:0 ]				addr_h, //行地址
                    output			reg		[ 11:0 ]				addr_v,  //列地址
                    output			wire		[ 4:0 ]				rgb_r,  //红基色
                    output			wire		[ 5:0 ]				rgb_g,  //绿基色
                    output			wire		[ 4:0 ]				rgb_b  //蓝基色
    );

// 640 * 480 60HZ
localparam	 H_FRONT = 16; // 行同步前沿信号周期长
localparam	 H_SYNC  = 96; // 行同步信号周期长
localparam	 H_BLACK = 48; // 行同步后沿信号周期长
localparam	 H_ACT   = 640; // 行显示周期长
localparam	 V_FRONT = 11; // 场同步前沿信号周期长
localparam	 V_SYNC  = 2; // 场同步信号周期长
localparam	 V_BLACK = 31; // 场同步后沿信号周期长
localparam	 V_ACT   = 480; // 场显示周期长

// 800 * 600 72HZ
// localparam	 H_FRONT = 40; // 行同步前沿信号周期长
// localparam	 H_SYNC  = 120; // 行同步信号周期长
// localparam	 H_BLACK = 88; // 行同步后沿信号周期长
// localparam	 H_ACT   = 800; // 行显示周期长
// localparam	 V_FRONT = 37; // 场同步前沿信号周期长
// localparam	 V_SYNC  = 6; // 场同步信号周期长
// localparam	 V_BLACK = 23; // 场同步后沿信号周期长
// localparam	 V_ACT   = 600; // 场显示周期长


localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期

reg			[ 11:0 ]			cnt_h			; // 行计数器
reg			[ 11:0 ]			cnt_v			; // 场计数器
reg			[ 15:0 ]			rgb			; // 对应显示颜色值

// 对应计数器开始、结束、计数信号
wire							flag_enable_cnt_h			;
wire							flag_clear_cnt_h			;
wire							flag_enable_cnt_v			;
wire							flag_clear_cnt_v			;
wire							flag_add_cnt_v  			;
wire							valid_area      			;


// 25M时钟 行周期*场周期*刷新率 = 800 * 525 * 60
reg							clk_25			;
// 50M时钟 1040 * 666 * 72
wire							clk_50			;
//PLL
// pll	pll_inst (
// 	.areset ( ~rst_n ),
// 	.inclk0 ( clk ),
// 	.c0 ( clk_50 ), //50M
// 	.c1 ( clk_25 ) //25M
// 	);
//根据不同分配率选择不同频率时钟
assign vga_clk = clk;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_25 <= 0;
    end
    else  begin
        clk_25 <= ~clk_25;
    end
end
// 行计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_h <= 0;
    end
    else if ( flag_enable_cnt_h ) begin
        if ( flag_clear_cnt_h ) begin
            cnt_h <= 0;
        end
        else begin
            cnt_h <= cnt_h + 1;
        end
    end
    else begin
        cnt_h <= 0;
    end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;

// 行同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        h_sync <= 1;
    end
    else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
        h_sync <= 0;
    end
    else if ( flag_clear_cnt_h ) begin // 其余为0
        h_sync <= 1;
    end
    else begin
        h_sync <= h_sync;
    end
end

// 场计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_v <= 0;
    end
    else if ( flag_enable_cnt_v ) begin
        if ( flag_clear_cnt_v ) begin
            cnt_v <= 0;
        end
        else if ( flag_add_cnt_v ) begin
            cnt_v <= cnt_v + 1;
        end
        else begin
            cnt_v <= cnt_v;
        end
    end
    else begin
        cnt_v <= 0;
    end
end
assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v    = flag_clear_cnt_h;

// 场同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        v_sync <= 1;
    end
    else if ( cnt_v == V_SYNC - 1 ) begin
        v_sync <= 0;
    end
        else if ( flag_clear_cnt_v ) begin
        v_sync <= 1;
        end
    else begin
        v_sync <= v_sync;
    end
end

// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_h <= 0;
    end
    else if ( valid_area ) begin
        addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
    end
    else begin
        addr_h <= 0;
    end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_v <= 0;
    end
    else if ( valid_area ) begin
        addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
    end
    else begin
        addr_v <= 0;
    end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;


// 显示颜色
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rgb <= 16'h0;
    end
    else if ( valid_area ) begin
        rgb <= rgb_data;
    end
    else begin
        rgb <= 16'b0;
    end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];

endmodule // vga_dirve

2.vga数据控制模块

横坐标为时间,纵坐标为距离大小。距离大小决定场坐标,第几个距离决定行坐标。

module data_drive (input			wire						clk,
                    input			wire						vga_clk,
                   input			wire						rst_n,
                   input			wire		[ 11:0 ]		addr_h,
                   input			wire		[ 11:0 ]		addr_v,
                   input			wire						data_vld,
                   input			wire		[ 23:0 ]		distance_data,
                   output			wire		[ 15:0 ]		rgb_data);

localparam	red    = 16'd63488;
localparam	orange = 16'd64384;
localparam	yellow = 16'd65472;
localparam	green  = 16'd1024;
localparam	blue   = 16'd31;
localparam	indigo = 16'd18448;
localparam	purple = 16'd32784;
localparam	white  = 16'd65503;
localparam	black  = 16'd0;

parameter	NUM = 100;
reg			[ 19:0 ]			distance_data_r			;
reg			[ 15:0 ]			rgb_data_r			;
reg			[ 10:0 ]			data_r[NUM -1:0]			;
integer j;
integer i;
//寄存数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        distance_data_r <= 0;
    end
    else if(data_vld) begin
        distance_data_r <=distance_data[7:4]+ distance_data[11:8]*10 + distance_data[15:12] * 100 ;
    end
end

//数据打拍
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        for (i = 0; i<NUM - 1;i=i+1 ) begin
            data_r[i] <= 0;
        end
    end
    else if(data_vld) begin
        data_r[0] <= distance_data_r;
        for (i = 1; i<NUM - 1;i=i+1 ) begin
            data_r[i] <= data_r[i-1];
        end
    end
end
reg			[ 10:0 ]			cnt			;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rgb_data_r = 0;
        cnt = 0;
    end
    else if(cnt == NUM) begin
        rgb_data_r = black;
        cnt = 0;
    end
    else if(addr_v > 470 && addr_v < 476 && addr_h >9 && addr_h < 625 ) begin //横坐标
        rgb_data_r = white;
        cnt = cnt;
    end
    else if(addr_h > 9 && addr_h <15 && addr_v >9 && addr_v <= 470) begin //纵坐标
        rgb_data_r = white;
        cnt = cnt;
    end
    else if(addr_h >20 && addr_h < 620 && addr_v >10 && addr_v < 470) begin  //打点
        if ( (cnt+1) * 3 == addr_h -20) begin
            if(data_r[cnt] == 470 - addr_v)begin
                rgb_data_r = red;
                cnt = cnt + 1;
            end
            else begin
                rgb_data_r = black;
                cnt = cnt + 1;
            end
        end
        else begin
            rgb_data_r = black;
            cnt = cnt;
        end
    end
    else begin
        rgb_data_r = black;
        cnt = cnt;
    end
end
assign rgb_data = rgb_data_r;
endmodule

六、数码管

基于FPGA的超声波测距_第7张图片

1.数码管段选控制

根据位选信号来显示对应位置的数字

module seg_drive(input			wire						clk,
                 input			wire						rst_n,
                 input			wire						data_vld,
                 input			wire		[ 23:0 ]		display_data,
                 input			wire		[ 5:0 ]		    sel,
                 output			reg		[ 7:0 ]				seg);
        

    localparam	ZERO  = 7'b100_0000;
    localparam	ONE   = 7'b111_1001;
    localparam	TWO   = 7'b010_0100;
    localparam	THREE = 7'b011_0000;
    localparam	FOUR  = 7'b001_1001;
    localparam	FIVE  = 7'b001_0010;
    localparam	SIX   = 7'b000_0010;
    localparam	SEVEN = 7'b111_1000;
    localparam	EIGHT = 7'b000_0000;
    localparam	NINE  = 7'b001_0000;
    localparam	N     = 7'b010_1011;
    localparam	P     = 7'b000_1111;

    reg			[ 23:0 ]			display_data_r			;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            display_data_r <= 0;
        end
        else if(data_vld) begin
            display_data_r <= display_data;
        end
    end

    reg dot;
    reg [ 3:0 ] num;

    always@( * ) begin
        case( sel )
            6'b111_110: begin
                // num = display_data / 100000 % 10;
                num = display_data_r[23 :20];
                dot = 1;                
            end
            6'b111_101: begin
                // num = display_data / 10000 % 10;
                num = display_data_r[19 : 16];
                dot = 1;
            end
            6'b111_011: begin
                // num = display_data / 1000 % 10;
                num = display_data_r[15 : 12];
                dot = 1;
            end
            6'b110_111: begin
                // num = display_data / 100 % 10;
                num = display_data_r[11 :8];
                dot = 0;
            end
            6'b101_111: begin
                //num = display_data / 10 % 10;
                num = display_data_r[7 :4];
                dot = 1;
            end
            6'b011_111: begin
                //num = display_data % 10;
                num = display_data_r[3 :0];
                dot = 1;
            end
            default num = 4'hf; 
        endcase
    end
    
    always @ ( * ) begin
        case( num )
            4'd0:   seg = {dot,ZERO}; // 匹配到后参考共阳极真值表
            4'd1:   seg = {dot,ONE};
            4'd2:   seg = {dot,TWO};
            4'd3:   seg = {dot,THREE};
            4'd4:   seg = {dot,FOUR};
            4'd5:   seg = {dot,FIVE};
            4'd6:   seg = {dot,SIX};
            4'd7:   seg = {dot,SEVEN};
            4'd8:   seg = {dot,EIGHT};
            4'd9:   seg = {dot,NINE};
            default : seg = {1'b0,ZERO};
        endcase
    end
endmodule

2.数码管位选

每20000ns刷新一次数码管

module sel_drive(
	input clk,
	input rst_n,
	
	output reg [5:0] sel
);
localparam state0 = 3'd0;
localparam state1 = 3'd1;
localparam state2 = 3'd2;
localparam state3 = 3'd3;
localparam state4 = 3'd4;
localparam state5 = 3'd5;

parameter	MAX_NUM = 1_000;

reg [2:0] current_state;
reg [2:0] next_state;
reg    [20:0]	  cnt; //时钟分频计数器
reg              flag;

//计数器
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		flag <= 1'b0;
		cnt <= 0;
	end
	else if(cnt == 0)begin//一轮计数完毕
		flag <= 1'b1;
		cnt <= 1;
	end
	else	begin 
		flag <= 1'b0;
		cnt <= (cnt + 1'b1) % MAX_NUM;//循环+1
	end
end
// 状态跳转
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		current_state <= state0;
	end
	else if(flag) begin
		current_state <= next_state;
	end
end


//状态判断
always @(*) begin
	if(!rst_n) begin
		next_state <= state0;
	end
	else if(flag) begin
		case(current_state)
			state0: begin
				next_state <= state1;
			end
			state1: begin
				next_state <= state2;
			end
			state2: begin
				next_state <= state3;
			end
			state3: begin
				next_state <= state4;
			end
			state4: begin
				next_state <= state5;
			end
			state5: begin
				next_state <= state0;
			end
			default:begin
				next_state <= state0;
			end
		endcase
	end
	else begin
		next_state <= next_state;
	end
end


//状态输出
always@(current_state) begin
	case (current_state)
		state0: begin
			sel <= 6'b011111;
		end
		state1: begin
			sel <= 6'b101111;
		end
		state2: begin
			sel <= 6'b110111;
		end
		state3: begin
			sel <= 6'b111011;
		end
		state4: begin
			sel <= 6'b111101;
		end
		state5: begin
			sel <= 6'b111110;
		end
		default:begin
			sel <= 6'b111111;
		end
	endcase


end
endmodule

七、顶层文件

由于数码管需要25M时钟,超声波测距需要1M时钟,通过PLL分频得到对应的时钟。

module distance_top (
    input			wire						clk,
    input			wire						rst_n,
    input			wire						echo,
    
    output			wire						trig,
    output			wire		[ 5:0 ]			sel,
    output			wire		[ 7:0 ]			seg,
    output			reg		    [ 3:0 ]			led,
    output			wire						beep,
    input			wire						key,
    output			wire						h_sync,
    output			wire						v_sync,
    output			wire		[ 4:0 ]			rgb_r,
    output			wire		[ 5:0 ]			rgb_g,
    output			wire		[ 4:0 ]			rgb_b,
    input			wire						rx_data,
    output			wire						tx_data
);

wire							clk_50			;
wire							clk_1			;
wire							clk_25			;
wire		[ 23:0 ]			distance_data			;
wire							data_out_vld			;
reg							    beep_vld			;
wire							key_out			;
wire		[ 15:0 ]			rgb_data			;
wire		[ 11:0 ]		    addr_h;
wire		[ 11:0 ]		    addr_v;
pll	pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0 ( clk_50 ),
	.c1 ( clk_1 ),
	.c2 ( clk_25 )
	);

//vga
vga_dirve u_vga_dirve(
    .clk      ( clk_25   ),
    .rst_n    ( rst_n    ),
    .rgb_data ( rgb_data ),
    .vga_clk  ( vga_clk  ),
    .h_sync   ( h_sync   ),
    .v_sync   ( v_sync   ),
    .addr_h   ( addr_h   ),
    .addr_v   ( addr_v   ),
    .rgb_r    ( rgb_r    ),
    .rgb_g    ( rgb_g    ),
    .rgb_b    ( rgb_b    )
);
//vag数据
data_drive u_data_drive(
    .clk           (clk),
    .vga_clk       ( vga_clk       ),
    .rst_n         ( rst_n         ),
    .addr_h        ( addr_h        ),
    .addr_v        ( addr_v        ),
    .data_vld      ( data_out_vld   ),
    .distance_data ( distance_data ),
    .rgb_data      ( rgb_data      )
);
//数码管
seg_drive u_seg_drive(
    .clk          ( clk          ),
    .rst_n        ( rst_n        ),
    .data_vld     ( data_out_vld ),
    .display_data ( distance_data),
    .sel          ( sel          ),
    .seg          ( seg          )
);
sel_drive u_sel_drive(
    .clk   ( clk_50   ),
    .rst_n ( rst_n ),
    .sel   ( sel   )
);
//测距
distance_drive u_distance(
    .clk           ( clk           ),
    .clk_1          (clk_1),
    .rst_n         ( rst_n         ),
    .echo          ( echo          ),
    .trig          ( trig          ),
    .data_out_vld  ( data_out_vld ),
    .distance_data ( distance_data )
);
//串口
uart_drive u_uart_drive(
    .clk           ( clk           ),
    .rst_n         ( rst_n         ),
    .distance_data ( distance_data ),
    .data_vld      ( data_out_vld   ),
    .rx_data       ( rx_data       ),
    .tx_data       ( tx_data       )
);

//蜂鸣器
beep_dirve u_beep_dirve(
    .clk           ( clk           ),
    .rst_n         ( rst_n         ),
    .beep_vld      ( beep_vld      ),
    .data_vld      ( data_out_vld      ),
    .distance_data ( distance_data ),
    .beep          ( beep          )
);
//按键消抖
key_debounce#(.KEY_W   ( 1 )) u_key_debounce(
    .clk     ( clk     ),
    .rst_n   ( rst_n   ),
    .key_in  ( key  ),
    .key_out  ( key_out  )
);

//控制蜂鸣器使能
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        beep_vld <= 0;
    end
    else if(key_out) begin
        beep_vld <= ~beep_vld;
    end
end
reg			[ 27:0 ]			cnt			        ;
// led
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt <= 0;
    end
    else if ( cnt == 50_000_000 - 1 ) begin
        cnt <= 0;
    end
    else begin
        cnt <= cnt + 1;
    end
end

always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        led <= 4'b0000;
    end
    else if ( cnt == 50_000_000 -1 )begin
        led <= ~led;
    end
    else begin
        led <= led;
    end
end
endmodule //distance_top

八、源代码

https://github.com/TangtangSix/distance

你可能感兴趣的:(FPGA,fpga开发)