Rango ultrasónico basado en FPGA - pantalla de tubo digital


prefacio

Entorno:
1. Quartus18.1
2. vscode
3. Modelo de placa: EP4CE6F17C8N
4. Módulo ultrasónico: HC_SR04
Requisitos:
utilice la placa de desarrollo EP4CE6F17C8 para controlar el módulo de detección ultrasónica (HC_SR04) y mostrar los datos medidos en el tubo digital en el desarrollo tablero superior


1. Introducción al módulo ultrasónico

1. Características del producto

El módulo de rango ultrasónico HC-SR04 puede proporcionar una función de detección de distancia sin contacto de 2 cm a 400 cm, y la precisión del rango puede alcanzar hasta 3 mm; el módulo incluye un transmisor ultrasónico, un receptor y un circuito de control.
Principio de funcionamiento básico:

(1) Use el puerto IO TRIG para activar el rango y proporcione una señal de alto nivel de al menos 10us.
(2) El módulo envía automáticamente 8 ondas cuadradas de 40 khz y detecta automáticamente si hay un retorno de señal;
(3) Si hay un retorno de señal, se emite un nivel alto a través del puerto IO ECHO y la duración del alto El nivel es el tiempo desde la transmisión hasta el regreso de la onda ultrasónica. Distancia de prueba = (tiempo de alto nivel * velocidad del sonido (340M/S))/2;

inserte la descripción de la imagen aquí

2. El diagrama de tiempo del módulo ultrasónico.

inserte la descripción de la imagen aquí

El diagrama de tiempo anterior muestra que solo necesita proporcionar una señal de disparo de pulso de más de 10uS, y el módulo enviará internamente 8 niveles periódicos de 40kHz y detectará ecos. Una vez que se detecta una señal de eco, se emite una señal de eco. El ancho de pulso de la señal de eco es proporcional a la distancia medida. Por lo tanto, la distancia se puede calcular a partir del intervalo de tiempo entre el envío de la señal y la recepción de la señal de eco.

2. Diseño del sistema

1. Diagrama de bloques del sistema

inserte la descripción de la imagen aquí

2. Código fuente

  • Archivo de nivel superior HC_SR04_TOP:
module 	HC_SR04_TOP(
	input  			clk		, 
	input   		rstn	, 
	
	input   		echo	, // 距离信号
	output   		trig	, // 触发测距信号
	output  wire [5:0]	sel	,
	output  wire [7:0]	seg
);

	wire	[18:00]		data_o		;
	wire 				clk_us		;

	seg_driver u_seg_driver(  
		.clk		(clk	),
		.rstn		(rstn	),
		.data_in	(data_o	), //待显示数据
		.sel		(sel	),	// 我这里是8位段选,可以换6位,但是要自己改代码
    	.seg		(seg	)     
	);	

	clk_div	u_clk_div(
		.clk		(clk	), 
		.rstn		(rstn	),
		.clk_us		(clk_us )
	);
	trig_driver	u_trig_driver(
		.clk_us		(clk_us	),
		.rstn		(rstn	),
		.trig		(trig	)
	);

	echo_driver	u_echo_driver(
		.clk		(clk	),
		.clk_us		(clk_us	),
		.rstn		(rstn	),
		.echo		(echo	),
		.data_o		(data_o	)
		);

//Logic Description
endmodule 

  • Módulo de accionamiento ultrasónico trig_driver:
module 	trig_driver(
	input  wire			clk_us	,
	input  wire 		rstn	,
		   
	output wire  		trig	  //触发测距信号
);

	parameter CYCLE_MAX = 19'd29_9999;

	reg		[18:00]	cnt		;

// 10毫秒持续电平输出
	
	always @(posedge clk_us or negedge rstn) begin
		if(!rstn) begin
			cnt <= 19'd0;
		end
		else if(cnt == CYCLE_MAX) begin
			cnt <= 19'd0;
		end
		else begin
			cnt <= cnt + 19'd1;
		end
	end
	
	assign trig = cnt < 15 ? 1'b1 : 1'b0;

endmodule 
  • módulo de rango echo_driver:
module 	echo_driver(
	input  wire 		clk		,
	input  wire			clk_us	,
	input  wire 		rstn	,
		   
	input  wire 		echo	,
	output wire [18:00]	data_o	  //检测距离,保留3位小数,*1000实现
);

	parameter T_MAX = 16'd5_9999;//510cm 对应计数值

	reg				r1_echo,r2_echo; //边沿检测	
	wire			echo_pos,echo_neg; //
	
	reg		[15:00]	cnt		; 
	
	reg		[18:00]	data_r	;
	
	//如果使用clk_us 检测边沿,延时2us,差值过大
	always @(posedge clk or negedge rstn)begin  
		if(!rstn)begin  
			r1_echo <= 1'b0;
			r2_echo <= 1'b0;
		end  
		else begin  
			r1_echo <= echo;
			r2_echo <= r1_echo;
		end  
	end
	
	assign echo_pos = r1_echo & ~r2_echo;
	assign echo_neg = ~r1_echo & r2_echo;
	
	always @(posedge clk_us or negedge rstn) begin
		if(!rstn) begin
			cnt <= 16'd0;
		end
		else if(echo) begin
			if(cnt == T_MAX) begin
				cnt <= 16'd0;
			end
			else begin
				cnt <= cnt + 16'd1;
			end
		end
		else begin
			cnt <= 16'd0;
		end
	end
	
	always @(posedge clk or negedge rstn)begin  
		if(!rstn)begin  
			data_r <= 'd2;
		end  
		else if(echo_neg)begin  
			data_r <= (cnt << 4) + cnt;
		end  
		else begin  
			data_r <= data_r;
		end  
	end
	
	assign data_o = data_r >> 1;

endmodule 

  • Módulo de división de frecuencia de reloj:
module 	clk_div(
	input  wire			clk		,
	input  wire 		rstn	,
		   
	output wire  		clk_us 	  //
);

	parameter CNT_MAX = 19'd49;//1us的计数值为 50 * Tclk(20ns)

	reg		[5:0]	cnt		; 
	wire			add_cnt ;
	wire			end_cnt ;
	
	// 时钟分频
	always @(posedge clk or negedge rstn) begin
		if(!rstn) begin
			cnt <= 6'd0;
		end
		else if(cnt == CNT_MAX) begin
			cnt <= 6'd0;
		end
		else begin
			cnt <= cnt + 6'd1;
		end
	end
	
	assign clk_us = cnt >= CNT_MAX ;
	

endmodule 

  • módulo controlador de tubo digital seg_driver:
module seg_driver(  
	input	wire		clk		,
	input	wire		rstn	,
	
	input	wire [18:0]	data_in	, //待显示数据

    output  reg [5:0]   sel     ,	// 我这里是8位段选,可以换6位,但是要自己改代码
    output  reg [7:0]   seg     
);								  
 	//parameter define  
	localparam	NUM_0	=	8'b1100_0000,	
				NUM_1 	= 	8'b1111_1001,
				NUM_2   = 	8'b1010_0100,
				NUM_3   = 	8'b1011_0000,
				NUM_4   = 	8'b1001_1001,
				NUM_5   = 	8'b1001_0010,
				NUM_6   = 	8'b1000_0010,
				NUM_7   = 	8'b1111_1000,
				NUM_8   = 	8'b1000_0000,
				NUM_9   = 	8'b1001_0000,
				NUM_A   = 	8'b1000_1000,
				NUM_B   = 	8'b1000_0011,
				NUM_C   = 	8'b1100_0110,
				NUM_D   = 	8'b1010_0001,
				NUM_E   = 	8'b1000_0110,
				NUM_F   = 	8'b1000_1110,
				ALL_LIGHT = 8'b0000_0000,
				LIT_OUT = 	8'b1111_1111,
                LINE    =   8'b1011_1111;

    localparam MAX_10us     =   10'd999     ;
 	//reg 、wire define		
	reg		[3:0]	cm_hund	    ;//100cm
	reg		[3:0]	cm_ten	    ;//10cm
	reg		[3:0]	cm_unit	    ;//1cm
	reg		[3:0]	point_1	    ;//1mm
	reg		[3:0]	point_2	    ;//0.1mm
	reg		[3:0]	point_3	    ;//0.01mm
    reg     [9:0]   cnt_10us    ;
    reg     [7:0]   num         ;// 段选输出判断

	always @(posedge clk or negedge rstn)begin  
		if(!rstn)begin  
			cm_hund	<= 'd0;
			cm_ten	<= 'd0;
			cm_unit	<= 'd0;
			point_1	<= 'd0;
			point_2	<= 'd0;
			point_3	<= 'd0;
		end  
		else begin  
			cm_hund <= data_in / 10 ** 5;
			cm_ten	<= data_in / 10 ** 4 % 10;
			cm_unit <= data_in / 10 ** 3 % 10;
			point_1 <= data_in / 10 ** 2 % 10;
			point_2 <= data_in / 10 ** 1 % 10;
			point_3 <= data_in / 10 ** 0 % 10;
		end  
	end 

    // 修改后   段选

	always @(posedge clk or negedge rstn) begin
		if(!rstn) begin
			cnt_10us <= 10'd0;
		end
		else if(cnt_10us == MAX_10us) begin
			cnt_10us <= 10'd0;
		end
		else begin
			cnt_10us <= cnt_10us + 10'd1;
		end
	end

    // 数码管位移
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            sel <= 6'b111_110;
        end
        else if(cnt_10us == MAX_10us) begin
            sel <= {
    
    sel[0],sel[5:1]};
        end
        else begin
            sel <= sel;
        end
    end

    // 确定输出数字
    always @(*) begin
        case (sel)
			6'b01_1111	:    num = hex_data(point_3);
			6'b10_1111	:    num = hex_data(point_2);
			6'b11_0111	:    num = hex_data(point_1);
			6'b11_1011	:    num = hex_data(cm_unit);
			6'b11_1101	:    num = hex_data(cm_ten)	;
			6'b11_1110	:    num = hex_data(cm_hund);
			// 6'b11_1111	:    num = LINE;
			// 6'b11_1111	:    num = LIT_OUT;

            default         :    num = NUM_0;
        endcase
    end

    // 位选输出
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            seg <= LINE;
        end
        else begin
            case (num)
                NUM_0       :   seg <= NUM_0    ;
                NUM_1       :   seg <= NUM_1    ;
                NUM_2       :   seg <= NUM_2    ;
                NUM_3       :   seg <= NUM_3    ;
                NUM_4       :   seg <= NUM_4    ;
                NUM_5       :   seg <= NUM_5    ;
                NUM_6       :   seg <= NUM_6    ;
                NUM_7       :   seg <= NUM_7    ;
                NUM_8       :   seg <= NUM_8    ;
                NUM_9       :   seg <= NUM_9    ;
                LINE        :   seg <= LINE     ;
                LIT_OUT     :   seg <= LIT_OUT  ;
                ALL_LIGHT   :   seg <= ALL_LIGHT;
            endcase
        end
    end

    // 函数,4位输入,7位输出,判断要输出的数字
    function  [7:0]	hex_data; //函数不含时序逻辑相关
		input   [03:00]	data_i;//至少一个输入
		begin
			case(data_i)
				4'd0:hex_data = NUM_0;
				4'd1:hex_data = NUM_1;
				4'd2:hex_data = NUM_2;
				4'd3:hex_data = NUM_3;
				4'd4:hex_data = NUM_4;
				4'd5:hex_data = NUM_5;
				4'd6:hex_data = NUM_6;
				4'd7:hex_data = NUM_7;
				4'd8:hex_data = NUM_8;
				4'd9:hex_data = NUM_9;
				default:hex_data = ALL_LIGHT;
			endcase	
		end 
	endfunction

endmodule  

3. Vista RTL

inserte la descripción de la imagen aquí

4. Efecto

Alcance ultrasónico


3. Resumen

Los primeros tres dígitos del tubo nixie representan unidades centimétricas de cientos, decenas y centímetros, y los últimos tres dígitos son lugares decimales reservados. En términos generales, el módulo de medición de distancia es relativamente preciso. A través de esta operación, entiendo básicamente el principio de conducción del módulo ultrasónico y soy más competente en la operación del tubo digital.

4. Referencias

Basado en la placa de desarrollo DE2 115 para controlar el módulo de rango ultrasónico HC_SR04 [con código fuente]

Supongo que te gusta

Origin blog.csdn.net/qq_52215423/article/details/130751872
Recomendado
Clasificación