【FPGA实验四】基于DE2-115的驱动超声波模块(HC_SR04)数码管显示实验

项目说明

  1. 使用 DE2-115 开发板驱动超声波模块(HC_SR04 ),
  2. 驱动数码管模块将数据显示到开发板模块上,
  3. 或者将数据用串口通信的方式将数据显示到笔记本串口助手上,
  4. 蜂鸣器根据数据大小鸣叫。

超声波测距模块

模块原理说明

如果超声波模块前方没有障碍物,约4米之内,那当Trig引脚被提供10μs以上脉冲触发信号后,该模块内部发射(transmit)8个40kHz的超声波脉冲并检测回波,Echo引脚收到一个高电平并持续38毫秒,之后就会处于低电平状态,所以当得到38ms这个值时,我们就知道前方没有检测到障碍物了。

如果前方有障碍物,那在发射超声波脉冲后,信号就会反射回来,Echo引脚就会在接收到信号时立即切换到低电平状态,此时Echo引脚在高电平状态保持的时间就是信号发射并返回的所花的时间。

我们知道距离 = 速度 X 时间,要求出超声波模块到障碍物之间的距离,我们就需要知道速度和时间。速度就是 340 m/s,也就是声音在空气中传播的速度,转换城cm/μs单位就是0.034 cm/μs。时间的话,由于我们得到的是信号从发射到返回的时间值,所以需要除以 2 。最终得到的计算公式为:距离 = 0.034 cm/μs * 时间(μs) / 2

超声波模块(正面)
超声波模块(正面)
超声波模块(反面)
在这里插入图片描述
从上图我们可以看到超声波模块的4个引脚,它们的作用罗列如下:

  1. VCC: 电源引脚,超声波模块工作电压为5伏。

  2. Trig: 是Trigger(触发)这个单词的缩写,该引脚用于触发超声波脉冲。

  3. Echo: 该引脚会在高电平和低电平之间转换,当检测到障碍物时,在高电平保持的时间就表示信号发射出去并反射回来的时间。

  4. GND: 接地引脚。

模块工作过程

(1) 主控设备给 Trig 脚提供一个 10us 的脉冲信号。

(2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。

(3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。

(4) Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。

模块工作时序图

在这里插入图片描述
以上时序图表明你只需要提供一个10uS 以上脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。公式: uS/58=厘米或者uS/148=英寸;或是:距离=高电平时间*声速(340M/S)/2;建议测量周期为60ms 以上,以防止发射信号对回响信号的影响。

模块电气参数

在这里插入图片描述
图源:HC-SR04超声波测距模块说明书(by深圳市捷深科技有限公司)

模块驱动代码

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

串口通信模块

该模块实现将采集到的测距数据通过串口将数据输出显示到笔记本的串口助手上的功能。

串口发送模块

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

串口发送控制模块

串口控制模块把接受到的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

数码管显示模块

所谓段选信号,就是给七段数码管的每一段灯管赋值,实现控制显示输出的效果。而位选信号,则是直接给整个七段数码管赋值,实现显示输出的效果。
在这里插入图片描述

数码管段选控制

根据位选信号来显示对应位置的数字。理解:seg(segment)

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


数码管位选控制

该模块每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分频得到对应的时钟。

RTL视图

在这里插入图片描述

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

总结

本次实验的综合性较强,结合了企业实训学过的蜂鸣器、数码管、模块,同时复习了上学期串口通信原理的知识,本次实验可以拓展通过VGA接口在显示屏上进行打点输出,有兴趣的读者可以进行实验。

参考连接:
https://blog.csdn.net/qq_43546203/article/details/125281386
https://blog.csdn.net/qq_43546203/article/details/125282050
https://blog.csdn.net/qq_45659777/article/details/124717655
https://blog.csdn.net/qq_47281915/article/details/125592938

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是基于DE2-115平台VGA显示图片的详细步骤: 1. 打开Quartus II软件,创建一个新工程。 2. 在工程中添加一个VHDL文件,用于实现VGA控制器。下面是一个简单的VGA控制器代码示例: ``` library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity VGA_Controller is Port ( clk : in STD_LOGIC; h_sync : out STD_LOGIC; v_sync : out STD_LOGIC; red : out STD_LOGIC_VECTOR (3 downto 0); green : out STD_LOGIC_VECTOR (3 downto 0); blue : out STD_LOGIC_VECTOR (3 downto 0); x : out STD_LOGIC_VECTOR (9 downto 0); y : out STD_LOGIC_VECTOR (9 downto 0)); end VGA_Controller; architecture Behavioral of VGA_Controller is signal vga_clk : std_logic; signal h_count : integer range 0 to 799 := 0; signal v_count : integer range 0 to 524 := 0; begin vga_clk <= clk; process(vga_clk) begin if rising_edge(vga_clk) then if h_count = 799 then h_count <= 0; if v_count = 524 then v_count <= 0; else v_count <= v_count + 1; end if; else h_count <= h_count + 1; end if; end if; end process; h_sync <= '1' when (h_count >= 656 and h_count <= 752) else '0'; v_sync <= '1' when (v_count >= 490 and v_count <= 492) else '0'; red <= "1111"; green <= "0000"; blue <= "0000"; x <= std_logic_vector(to_unsigned(h_count, 10)); y <= std_logic_vector(to_unsigned(v_count, 10)); end Behavioral; ``` 这个VGA控制器代码示例中,使用了默认的640x480分辨率和60Hz刷新率。其中,h_sync和v_sync分别表示水平同步信号和垂直同步信号,red、green和blue分别表示红、绿、蓝三个颜色通道,x和y表示当前像素的坐标。 3. 在工程中添加一个图片文件,将其转换为灰度图像并调整分辨率为640x480。可以使用Matlab等工具进行图像处理和转换。 4. 在VGA控制器代码中添加一个ROM模块,用于存储转换后的图像数据。下面是一个简单的ROM模块代码示例: ``` library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ROM is Port ( clk : in STD_LOGIC; address : in STD_LOGIC_VECTOR (17 downto 0); data : out STD_LOGIC_VECTOR (7 downto 0)); end ROM; architecture Behavioral of ROM is type ROM_array is array (0 to 307199) of std_logic_vector(7 downto 0); constant ROM_data : ROM_array := ( -- 图像数据 ); begin process(clk) begin if rising_edge(clk) then data <= ROM_data(to_integer(unsigned(address))); end if; end process; end Behavioral; ``` 在ROM模块中,使用一个ROM_array类型的常量存储转换后的图像数据,通过address输入读取对应的像素数据。 5. 在VGA控制器代码中,将ROM模块的输出与红、绿、蓝三个颜色通道连接起来,实现将像素数据输出到VGA显示器。 6. 在Quartus II软件中,进行引脚分配,将VGA控制器的输出信号与DE2-115板子上的VGA接口相连。 7. 编译工程生成.sof文件,并将其下载到DE2-115板子中。 完成以上步骤后,即可在DE2-115的VGA显示器上看到输出的图像。需要注意的是,因为DE2-115的FPGA资源有限,可能无法一次性显示完整的640x480分辨率的图像。可以将图像分成若干个区域分别输出,或者降低分辨率以适应FPGA资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值