京微齐力:基于H7的曼彻斯特(编码解码串口)系统

news2024/11/15 18:26:38

目录

  • 前言
  • 一、关于曼彻斯特编码
  • 二、任务&实验效果
  • 三、硬件选择
    • 1、H7P20N0L176-M2H1
    • 2、XC7A35TFGG484-2
  • 四、程序设计
    • 1、顶层模块
    • 2、编码&发送模块
    • 3、解码&接收模块
    • 4、HC595驱动模块
    • 5、段选&位选模块
  • 五、debugware 回环实验
  • 六、兼容设计
  • 七、工程获取

前言

    四月到现在一直比较忙,有一段时间没有做京微齐力器件的开发了,本次做一个新器件H7的曼彻斯特(编码&解码&通信)系统。

一、关于曼彻斯特编码

      曼彻斯特编码(Manchester coding),又称自同步码、相位编码(phase encoding,PE),能够用信号的变化来保持发送设备和接收设备之间的同步。值得一提的是,曼彻斯特编码有两种截然相反的约定:
      它用电压的变化来分辨0和1,从高电平到低电平的跳变代表1,而从低电平到高电平的跳变代表0(G.E.Tomas编码方式)。
      从高电平到低电平的跳变代表0,而从低电平到高电平的跳变代表1(IEEE 802.3编码方式),信号的保持不会超过一个比特位的时间间隔。即使是0或1的序列,信号也将在每个时间间隔的中间发生跳变。这种跳变将允许接收设备的时钟与发送设备的时钟保持一致。

两种曼彻斯特编码:
在这里插入图片描述
优点:
      与NRZ相比,曼彻斯特编码提供一种同步机制,保证发送端与接收端信号同步。
缺点:
      曼彻斯特编码的频率要比NRZ高一倍,传输等量数据所需的带宽大一倍。

编码:
      曼彻斯特编码是将时钟和数据包含在信号流中,在传输代码信息的同时,也将时钟同步信号一起传输到对方。曼彻斯特码是用“01”和“10”来表示普通二进制数据中的“1””和“0”的,因此在实际电路设计中,我们可以用采一个2选1数字选择器来完成此项功能。(IEEE 802.3编码方式)
在这里插入图片描述

解码:
      曼彻斯特译码电路设计的目的,是如何准确地从曼彻斯特码的码流中提取出“10”和“01”信号,并将其转换成普通二进制编码中的“0”和“1”。
      在实际设计电路中,可以采用一个缓存器,保存上一个时钟采集到的信号和当前时钟采集到的信号,当缓存器的内容是“01”时,输出“1”;当缓存器的内容是“10”时,输出“0”;当缓存器的内容是“00”或“11”时,输出维持不变。
在这里插入图片描述

曼彻斯特编码在FPGA领域的运用比较广泛:

      以太网:在以太网中,曼彻斯特编码被用来将数据转换为数字信号,并通过物理媒介(如同轴电缆或光纤)传输。

      远程控制系统:在无线遥控器中,曼彻斯特编码被用来将指令编码并通过无线信道发送给接收器。

      工业自动化系统:在工业自动化系统中,曼彻斯特编码被用来将传感器测量值、控制信号等转换为数字信号,并通过数据总线传输。

      汽车电子系统:在汽车电子系统中,曼彻斯特编码被用来将控制信号、传感器信号等转换为数字信号,并通过汽车网络传输。

曼彻斯特编码的时钟线与数据线结合一体,同步性高,错误检测性能好,去年在接触屏显项目的时候,便是用到了这一编码。

二、任务&实验效果

1、用两块FPGA开发板设计一个曼彻斯特码编码和解码系统;
2、第1块板负责在按键后将拨码开关拨出的8位二进制码用曼彻斯特码发出;
3、第2块板负责在收到曼彻斯特码号将其解析并在数码管上显示。两块板记得共地。(也可以用debugware进行波形读取)

进入正文前,先看实验效果:

京微齐力:基于H7的曼彻斯特(编码&解码&串口)系统

      可以看到,H7端按下发送键后,第二块开发板,接收到信号并进行解码,结果(1111_0000)显示在8位数码管上。(具体过程请看程序解析)

三、硬件选择

1、H7P20N0L176-M2H1

      本次实验使用H7作为主控板,HME-H7系列采用低功耗22nm 技术,集成了高性能ARM Cortex-M3 MCU(频率高达300M)、外围设备与大容量片上SRAM。本次实验只使用逻辑部分,后面根据需要再扩展MCU实验。
在这里插入图片描述

      H7具有12K的6输入查找表,1个OSC(80Mhz片内振荡器),128 个 10x10 DSP模块,18个32Kx32 位 SRAM,拥有多个封装,能够兼容Altera的EP4CE10和Xilinx Spartan-6,适用于伺候电机、图像处理及通信网络等多种场景。
      

在这里插入图片描述

2、XC7A35TFGG484-2

      因为H7板卡没有数码管,这里借助一块Spartan-7板卡展示实验效果。(如果没有多余板载数码管的开发板,可以使用FUXI软件的debugware IP进行波形观看)
在这里插入图片描述

四、程序设计

1、顶层模块

      本模块,实现的是H7对tx_data(8’b11110000)进行编码,按下按键(tx_en_n)后,会以UART的方式发送数据到第2块开发板 (这里模拟一个通信的效果,如果有Lora模块的同学,可以接上模块,这样就实现了无线通信)。关于编码部分,原本是计划使用拨码开关来模拟需要发送的8bit数据,但是板卡没有拨码开关,这里例化的时候,直接对端口进行赋值。程序里仍保留了拨码开关接口,有需要的同学,可以自行例化。

//**************************************** Message ***********************************//
//技术交流:bumianzhe@126.com
//关注CSDN博主:“千歌叹尽执夏”
//Author: 千歌叹尽执夏 
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// Target Devices: 		H7P20N0L176-M2H1
// Tool Versions:       Fuxi 2023.1
// File name:           hme_manchester
// Last modified Date:  2023年6月27日20:00:00
// Last Version:        V1.1
// Descriptions:        曼彻斯特解码&编码&串口
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module hme_manchester(
		input        sys_clk,    //高频时钟,可以是50MHz,本次入20MHz
		input        rst_n,
		input        rx_d,   //接收到的bit数据(本实验支持回环)
		output       tx_d,	 //将tx_data进行编码并以串口方式发送出去
		//发送使能
		input        tx_en_n,//发送使能,使用按键控制
		input [7:0]  tx_data,//发送的8bits数据,拨码开关控制
		output 		 sh_cp,  //串行数据输出
	    output 		 st_cp,	 //移位寄存器的时钟输出
	    output 		 ds		 //存储寄存器的时钟输出
);

wire [7:0] rx_data;
wire       rx_valid;
reg  [7:0] rx_data_dis;		 	//最终显示的接收数据
wire [7:0] sel;					//数码管位选(选择当前要显示的数码管)
wire [6:0] seg;					//数码管段选(当前要显示的内容)

pll_v1 u_pll_v1(
    .clkin0		(sys_clk),		//输入20MHz时钟
    .locked		(),
    .clkout0	(clk)			//输出50MHz时钟
);



manchester_tx u_manchester_tx_0(
		.clk(clk),    			//高频时钟,可以是50MHz
		.rst_n(rst_n),
	  //.tx_data(tx_data),  	//待发送的字节数据
		.tx_data(8'b11110000),  //由于板卡上没有拨码开关,这里直接输入数据进行模拟
		.tx_en(!tx_en_n),   	//发送使能
		.tx_ready(),
		.tx_d(tx_d)
		);
//如果没有两块板卡的,可以将manchester_tx的tx_d信号接到manchester_rx的rx_d
//形成回环实验,并利用debugware进行数据查看。
manchester_rx u_manchester_rx_0(
		.clk(clk),     			//高频时钟,可以是50MHz
		.rst_n(rst_n),
		.rx_d(rx_d),   			//接收到的bit数据
	  //.rx_d(tx_d),   			//回环实验
		.rx_data(rx_data),
		.rx_valid(rx_valid)
		);
		
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
    rx_data_dis <= 8'd0;
else
    rx_data_dis <= rx_valid?rx_data:rx_data_dis;
end

	
HC595_driver u_HC595_driver(
		.clk(clk),
		.reset_n(rst_n),
		.data({1'd0,seg,sel}),
		.s_en(1'b1),
		.sh_cp(sh_cp),
		.st_cp(st_cp),
		.ds(ds)
	);

Hex8 u_Hex8(
		.clk(clk),
		.reset_n(rst_n),
		.en(1'b1),
		.disp_data({3'd0,rx_data_dis[7],3'd0,rx_data_dis[6],3'd0,rx_data_dis[5],3'd0,rx_data_dis[4],3'd0,rx_data_dis[3],3'd0,rx_data_dis[2],3'd0,rx_data_dis[1],3'd0,rx_data_dis[0]}),
		.sel(sel),
		.seg(seg)
	);
//使用debugware查看编码&解码波形
debugware_v2_1 u_debugware_v2_1(
    .trig_out_0		(),
    .data_in_0		({tx_en_n,tx_d,rx_data}),
    .ref_clk_0		(clk)
);

endmodule 

2、编码&发送模块

      本模块,对需要发送的8bit数据进行曼彻斯特编码,并以串口的方式发送出去(波特率9600)。
      首先,当复位信号 rst_n 为低电平时,将会执行 if(rst_n==1’b0) 语句中的代码,对该模块中的所有寄存器进行初始化。
      接着,在每一个时钟周期的上升沿,如果时钟倍频使能信号clk_bps_en 为真,则将执行 case(state) 语句中当前状态下对应的代码。
      当状态机处于 IDLE 状态时,如果 tx_en 为真,则需要开始发送数据。此时,需要将状态切换为 TXS,将要发送的数据写入 tx_data_reg,设置 tx_ready_r 为低电平,并将 tx_cnt 设置为 0。
      当状态机处于 TXS 状态时,需要根据已经发送的数据字节数 tx_cnt 来进行数据编码,同时更新 tx_ready_r、tx_d_r 和 tx_data_reg 的值。

具体编码过程如下:
      当 tx_cnt 为 0 时,需要发送两个高电平(1bit数据编制成2bit数据)。当 tx_cnt 的值在 [1, 8] 之间时,需要按照 Manchester 编码方式编码数据并发送出去。

具体操作如下:
      根据 tx_data_reg 的最高位来计算当前发送的是 01 还是 10。如果最高位为 1,则当前发送的是 01,否则当前发送的是 10。对于 tx_cnt 中的每一位(从右往左数),如果是 0,则发送的是 10,否则发送的是 01。
      将 tx_data_reg 向左移动一位,为下一次计算做准备。
      当 tx_cnt 的值大于 8 时,需要发送一个低电平,因此将 tx_d_r 设置为低电平。
      如果 tx_cnt 达到了指定长度(这里是 19),则需要重新将状态切换为 IDLE,设置 tx_ready_r 为高电平,并将 tx_cnt 设置为 0。

//**************************************** Message ***********************************//
//技术交流:bumianzhe@126.com
//关注CSDN博主:“千歌叹尽执夏”
//Author: 千歌叹尽执夏 
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// Target Devices: 		H7P20N0L176-M2H1
// Tool Versions:       Fuxi 2023.1
// File name:           manchester_tx
// Last modified Date:  2023年6月17日9:00:00
// Last Version:        V1.1
// Descriptions:        编码&发送模块模块
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module manchester_tx(
		input       clk,    //高频时钟,可以是50MHz
		input       rst_n,
		input [7:0] tx_data,//待发送的字节数据
		input       tx_en,  //发送使能
		output      tx_ready,
		output      tx_d
		);
	parameter IDLE = 1'b0;//空闲状态
	parameter TXS  = 1'b1;//发送状态
	reg tx_ready_r;
	reg tx_d_r;
	reg state;
	reg [4:0] tx_cnt;//发送的bits计数器
	reg [7:0] tx_data_reg;
	wire clk_bps_en;
	assign tx_ready = tx_ready_r;
	assign tx_d = tx_d_r;
	precise_divider//分频模块
	#(
		//DEVIDE_CNT = 85.89934592 * fo  @50M
		//DEVIDE_CNT = 42.94967296 * fo  @100M
		.DEVIDE_CNT(32'd1649267)	//9600Hz * 2
	)u_precise_divider_0
	(
		//global clock
		.clk(clk),
		.rst_n(rst_n),
		
		//user interface
		//.divide_clk()
		.divide_clken(clk_bps_en)
	);
	always@(posedge clk or negedge rst_n)
	begin
	if(rst_n == 1'b0)
		begin
		state <= IDLE;
		tx_ready_r <= 1'b1;
		tx_d_r <= 1'b0;
		tx_data_reg <= 8'd0;
		tx_cnt <= 5'd0;
		end
	else if(clk_bps_en)
		begin
		case(state)
		IDLE:begin
			state <= tx_en?TXS:IDLE;
			tx_data_reg <= tx_en?tx_data:tx_data_reg;
			tx_ready_r <= tx_en?1'b0:1'b1;
			tx_cnt <= 5'd0;
		end
		TXS:begin
			tx_cnt <= (tx_cnt >= 5'd19)?5'd0:tx_cnt+1'b1;
			state <= (tx_cnt >= 5'd19)?IDLE:TXS;
			tx_ready_r <= (tx_cnt >= 5'd19)?1'b1:1'b0;
			if(tx_cnt[4:1] == 4'd0)
				tx_d_r <= 1'b1;//发2个高电平
			else if(tx_cnt[4:1] <= 4'd8)
				begin
				//如果tx_data_reg[7]的值为1,则将tx_cnt[0]的值赋给tx_d_r;
				//否则将tx_cnt[0]的逻辑反值(即0变成1,1变成0)赋给tx_d_r。
				tx_d_r <= tx_data_reg[7]?tx_cnt[0]:!tx_cnt[0];//1--01 0--10
				tx_data_reg <= tx_cnt[0]?(tx_data_reg<<1):tx_data_reg;
				end
			else begin
				tx_d_r <= 1'b0;
			end
		end
		endcase
		end
	end
endmodule
	

3、解码&接收模块

      接收到的串行数据进行解串行化,然后进行解码。
      解码与编码原理差不多,这里不做赘述。

//**************************************** Message ***********************************//
//技术交流:bumianzhe@126.com
//关注CSDN博主:“千歌叹尽执夏”
//Author: 千歌叹尽执夏 
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// Target Devices: 		H7P20N0L176-M2H1
// Tool Versions:       Fuxi 2023.1
// File name:           manchester_rx
// Last modified Date:  2023年6月18日15:00:00
// Last Version:        V1.1
// Descriptions:        解码&接收模块
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module manchester_rx(
		input        clk,    //高频时钟,可以是50MHz
		input        rst_n,
		input        rx_d,   //接收到的bit数据
		output [7:0] rx_data,
		output       rx_valid
);
		
	parameter IDLE = 1'b0;//空闲状态
	parameter RXS  = 1'b1;//接收状态
	reg state;
	reg rx_valid_r0,rx_valid_r;
	reg [15:0] rx_data_reg;//缓存16bits的rx数据,每2bits代表1bit数据//01:1 10:0
	reg [7:0]  rx_data_r;
	wire [7:0] rx_data_w;
	wire clk_bps_en;
	reg [3:0] rx_cnt;//对16倍波特率的时钟计数
	reg [3:0] byte_cnt;//对已接收的字节计数
	assign rx_data  = rx_data_r;
	assign rx_valid = rx_valid_r;
	
	//将一组长度为16的串行数据rx_data_reg转换成一组长度为8的并行数据rx_data_w,
	//即将接收到的串行数据进行解串行化。
	generate
		genvar i;
		for(i=0;i<8;i=i+1) begin:u1
			assign rx_data_w[i] = !rx_data_reg[i*2+1] && rx_data_reg[i*2];
		end
	endgenerate
	
	precise_divider//分频模块
	#(
		//DEVIDE_CNT = 85.89934592 * fo  @50M
		//DEVIDE_CNT = 42.94967296 * fo  @100M
		.DEVIDE_CNT(32'd13194139)	//9600Hz * 16
	)u_precise_divider_0
	(
		//global clock
		.clk(clk),
		.rst_n(rst_n),
		
		//user interface
		//.divide_clk()
		.divide_clken(clk_bps_en)
	);
	
	always@(posedge clk or negedge rst_n)
	begin
	if(rst_n == 1'b0)
		begin
		rx_data_r <= 8'd0;
		rx_valid_r <= 1'b0;
		end
	else
		begin
		rx_data_r <= rx_data_w;
		rx_valid_r <= rx_valid_r0;
		end
	end
	always@(posedge clk or negedge rst_n)
	begin
	if(rst_n == 1'b0)
		begin
		state <= IDLE;
		rx_data_reg <= 16'd0;
		
		rx_cnt <= 4'd0;
		byte_cnt <= 4'd0;
		rx_valid_r0 <= 1'b0;
		end
	else if(clk_bps_en)
		begin	 
		case(state)
		IDLE:begin
			rx_cnt      <= rx_d?(rx_cnt >= 4'd10)?4'd0:rx_cnt+1'b1:
						4'd0
						;
			state       <= (rx_cnt >= 4'd10)?RXS:IDLE;
			rx_data_reg <= 16'd0;
			byte_cnt    <= 4'd0;
			rx_valid_r0 <= 1'b0;
		end
		RXS:begin
			rx_cnt <= (rx_cnt >= 4'd7)?4'd0:rx_cnt+1'b1;
			if(rx_cnt >= 4'd7)
				rx_data_reg <= {rx_data_reg[14:0],rx_d};
			else 
				rx_data_reg <= rx_data_reg;
			byte_cnt <= (rx_cnt >= 4'd7)?byte_cnt+1'b1:byte_cnt;
			state <= ((rx_cnt >= 4'd7) && byte_cnt == 4'd15)?IDLE:RXS;
			rx_valid_r0 <= ((rx_cnt >= 4'd7) && byte_cnt == 4'd15);
		end
		endcase
		end
	end
endmodule
	

4、HC595驱动模块

      第二块板卡的电路设计用到了芯片 74HC595,该芯片的作用是移位寄存器,通过移位的方式,节省 FPGA 的管脚。FPGA 只需要输出 3 个管脚,即可达到发送数码管数据的目的,与传统的选位选方式相比,大大节省了 IO 设计资源。(考虑到部分同学,只有普通的8段数码管模块,没有74HC595,在文末会添加传统的数码管驱动代码,方便大家做兼容设计)。

//**************************************** Message ***********************************//
//技术交流:bumianzhe@126.com
//关注CSDN博主:“千歌叹尽执夏”
//Author: 千歌叹尽执夏 
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// Target Devices: 		H7P20N0L176-M2H1
// Tool Versions:       Fuxi 2023.1
// File name:           HC595_driver
// Last modified Date:  2023年6月11日20:00:00
// Last Version:        V1.1
// Descriptions:        驱动HC595,发出数据和选通信号
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module HC595_driver(
	clk,    
	reset_n,
	data,
	s_en,

	sh_cp,
	st_cp,
	ds
);

	input clk;
	input reset_n;
	input [15:0]data;
	input s_en;
	
	output reg sh_cp;
	output reg st_cp;
	output reg ds;
	
	assign reset=~reset_n;
	parameter CNT_MAX = 2;
	
	reg [15:0]r_data;
	always@(posedge clk)
	if(s_en)
		r_data <= data;

	reg [7:0]divider_cnt;//分频计数器;
	
	always@(posedge clk or posedge reset)
	if(reset)
		divider_cnt <= 0;
	else if(divider_cnt == CNT_MAX - 1'b1)
		divider_cnt <= 0;
	else
		divider_cnt <= divider_cnt + 1'b1;
		
	wire sck_plus;
	assign sck_plus = (divider_cnt == CNT_MAX - 1'b1);
		
	reg [5:0]SHCP_EDGE_CNT;
	
	always@(posedge clk or posedge reset)
	if(reset)
		SHCP_EDGE_CNT <= 0;
	else if(sck_plus)begin
		if(SHCP_EDGE_CNT == 6'd32)
			SHCP_EDGE_CNT <= 0;
		else
			SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
	end
	else
		SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
		
	always@(posedge clk or posedge reset)
	if(reset)begin
		st_cp <= 1'b0;
		ds <= 1'b0;
		sh_cp <= 1'd0;
	end 
	else begin
		case(SHCP_EDGE_CNT)
			0: begin sh_cp <= 0; st_cp <= 1'd0;ds <= r_data[15];end
			1: begin sh_cp <= 1; st_cp <= 1'd0;end
			2: begin sh_cp <= 0; ds <= r_data[14];end
			3: begin sh_cp <= 1; end
			4: begin sh_cp <= 0; ds <= r_data[13];end	
			5: begin sh_cp <= 1; end
			6: begin sh_cp <= 0; ds <= r_data[12];end	
			7: begin sh_cp <= 1; end
			8: begin sh_cp <= 0; ds <= r_data[11];end	
			9: begin sh_cp <= 1; end
			10: begin sh_cp <= 0; ds <= r_data[10];end	
			11: begin sh_cp <= 1; end
			12: begin sh_cp <= 0; ds <= r_data[9];end	
			13: begin sh_cp <= 1; end
			14: begin sh_cp <= 0; ds <= r_data[8];end	
			15: begin sh_cp <= 1; end
			16: begin sh_cp <= 0; ds <= r_data[7];end	
			17: begin sh_cp <= 1; end
			18: begin sh_cp <= 0; ds <= r_data[6];end	
			19: begin sh_cp <= 1; end
			20: begin sh_cp <= 0; ds <= r_data[5];end	
			21: begin sh_cp <= 1; end
			22: begin sh_cp <= 0; ds <= r_data[4];end	
			23: begin sh_cp <= 1; end
			24: begin sh_cp <= 0; ds <= r_data[3];end	
			25: begin sh_cp <= 1; end
			26: begin sh_cp <= 0; ds <= r_data[2];end	
			27: begin sh_cp <= 1; end
			28: begin sh_cp <= 0; ds <= r_data[1];end			
			29: begin sh_cp <= 1; end
			30: begin sh_cp <= 0; ds <= r_data[0];end
			31: begin sh_cp <= 1; end
			32: st_cp <= 1'd1;
			default:		
				begin
					st_cp <= 1'b0;
					ds <= 1'b0;
					sh_cp <= 1'd0;
				end
		endcase
	end

endmodule
	

5、段选&位选模块

在这里插入图片描述

      2个共阳极的7段4位数码管,采用动态扫描的显示方式,即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。
      比较简单的数码管段选、位选模块,这里不做赘述。

//**************************************** Message ***********************************//
//技术交流:bumianzhe@126.com
//关注CSDN博主:“千歌叹尽执夏”
//Author: 千歌叹尽执夏 
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// Target Devices: 		H7P20N0L176-M2H1
// Tool Versions:       Fuxi 2023.1
// File name:           Hex8
// Last modified Date:  2023年6月11日13:35:00
// Last Version:        V1.1
// Descriptions:        分频信号,段选和位选信号生成,输出需要显示的数据、段选和位选值
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module Hex8(
	clk,
	reset_n,
	en,
	disp_data,
	sel,
	seg
);
	assign reset=~reset_n;
	input clk;	//50M
	input reset_n;
	input en;	//数码管显示使能,1使能,0关闭
	
	input [31:0]disp_data;
	
	output [7:0] sel;//数码管位选(选择当前要显示的数码管)
	output reg [6:0] seg;//数码管段选(当前要显示的内容)
	
	reg [14:0]divider_cnt;//25000-1
	
	reg clk_1K;
	reg [7:0]sel_r;
	
	reg [3:0]data_tmp;//数据缓存

//	分频计数器计数模块
	always@(posedge clk or posedge reset)
	if(reset)
		divider_cnt <= 15'd0;
	else if(!en)
		divider_cnt <= 15'd0;
	else if(divider_cnt == 24999)
		divider_cnt <= 15'd0;
	else
		divider_cnt <= divider_cnt + 1'b1;

//1K扫描时钟生成模块		
	always@(posedge clk or posedge reset)
	if(reset)
		clk_1K <= 1'b0;
	else if(divider_cnt == 24999)
		clk_1K <= ~clk_1K;
	else
		clk_1K <= clk_1K;
		
//8位循环移位寄存器
	always@(posedge clk_1K or posedge reset)
	if(reset)
		sel_r <= 8'b0000_0001;
	else if(sel_r == 8'b1000_0000)
		sel_r <= 8'b0000_0001;
	else
		sel_r <=  sel_r << 1;
		
	always@(*)
		case(sel_r)
			8'b0000_0001:data_tmp = disp_data[3:0];
			8'b0000_0010:data_tmp = disp_data[7:4];
			8'b0000_0100:data_tmp = disp_data[11:8];
			8'b0000_1000:data_tmp = disp_data[15:12];
			8'b0001_0000:data_tmp = disp_data[19:16];
			8'b0010_0000:data_tmp = disp_data[23:20];
			8'b0100_0000:data_tmp = disp_data[27:24];
			8'b1000_0000:data_tmp = disp_data[31:28];
			default:data_tmp = 4'b0000;
		endcase
		
	always@(*)
		case(data_tmp)
			4'h0:seg = 7'b1000000;
			4'h1:seg = 7'b1111001;
			4'h2:seg = 7'b0100100;
			4'h3:seg = 7'b0110000;
			4'h4:seg = 7'b0011001;
			4'h5:seg = 7'b0010010;
			4'h6:seg = 7'b0000010;
			4'h7:seg = 7'b1111000;
			4'h8:seg = 7'b0000000;
			4'h9:seg = 7'b0010000;
		endcase
		
	assign sel = (en)?sel_r:8'b0000_0000;

endmodule

五、debugware 回环实验

      本节适用于只有一块板卡的同学,将发送模块的tx_d信号接到接收模块的rx_d信号,形成回环实验,并调用debugware进行数据分析。

//如果没有两块板卡的,可以将manchester_tx的tx_d信号接到manchester_rx的rx_d
//形成回环实验,并利用debugware进行数据查看。
manchester_rx u_manchester_rx_0(
		.clk(clk),     			//高频时钟,可以是50MHz
		.rst_n(rst_n),
	  //.rx_d(rx_d),   			//接收到的bit数据
		.rx_d(tx_d)				//回环实验
		.rx_data(rx_data),
		.rx_valid(rx_valid)
		);

例化一个debugware IP:
在这里插入图片描述

//使用debugware查看编码&解码波形
debugware_v2_1 u_debugware_v2_1(
    .trig_out_0		(),
    .data_in_0		({tx_en_n,tx_d,rx_data_dis}),	//数据拼接
    .ref_clk_0		(clk)
);

      从debugware的波形看,使能按键按下后,编码模块将预留的数据进行编码并以串口的形式发送,rx_data_dis显示解码后的数据。

在这里插入图片描述

六、兼容设计

      4.4节提到,为了方便只有传统数码管模块的同学进行设计,这里提供了传统的数码管驱动模块。
在这里插入图片描述

module hme_manchester(
		//其他信号-略
		//数码管
		output [6:0] odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7
);
//其他模块-略-自行复制补充

HEX HEX_u(
.idata({3'd0,rx_data_dis[7],3'd0,rx_data_dis[6],3'd0,rx_data_dis[5],3'd0,rx_data_dis[4],3'd0,rx_data_dis[3],3'd0,rx_data_dis[2],3'd0,rx_data_dis[1],3'd0,rx_data_dis[0]}),
.rst(1'b1),
.clk(clk),
.odata0(odata0),
.odata1(odata1),
.odata2(odata2),
.odata3(odata3),
.odata4(odata4),
.odata5(odata5),
.odata6(odata6),
.odata7(odata7)
);	
endmodule 
module HEX(idata,rst,clk,odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7);
input [31:0] idata ;
input clk,rst;
output [6:0] odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7;
wire [6:0] d0,d1,d2,d3,d4,d5,d6,d7;
reg [6:0] odata0_r,odata1_r,odata2_r,odata3_r,odata4_r,odata5_r,odata6_r,odata7_r;

assign odata0=odata0_r;
assign odata1=odata1_r;
assign odata2=odata2_r;
assign odata3=odata3_r;
assign odata4=odata4_r;
assign odata5=odata5_r;
assign odata6=odata6_r;
assign odata7=odata7_r;
SHEX SHEX0 (.idata(idata[3:0]),
            .rst(rst),
            .clk(clk),
            .odata(d0));
SHEX SHEX1 (.idata(idata[7:4]),
            .rst(rst),
            .clk(clk),
            .odata(d1));
SHEX SHEX2 (.idata(idata[11:8]),
            .rst(rst),
            .clk(clk),
            .odata(d2));
SHEX SHEX3 (.idata(idata[15:12]),
            .rst(rst),
            .clk(clk),
            .odata(d3));
SHEX SHEX4 (.idata(idata[19:16]),
            .rst(rst),
            .clk(clk),
            .odata(d4));
SHEX SHEX5 (.idata(idata[23:20]),
            .rst(rst),
            .clk(clk),
            .odata(d5));
SHEX SHEX6 (.idata(idata[27:24]),
            .rst(rst),
            .clk(clk),
            .odata(d6));
SHEX SHEX7 (.idata(idata[31:28]),
            .rst(rst),
            .clk(clk),
            .odata(d7)); 
always@(posedge clk or negedge rst)
 begin
  if(rst==1'b0)
   begin
    odata0_r<=7'd0;
    odata1_r<=7'd0;
    odata2_r<=7'd0;
    odata3_r<=7'd0;
    odata4_r<=7'd0;
    odata5_r<=7'd0;
    odata6_r<=7'd0;
    odata7_r<=7'd0;
   end
  else 
   begin
    odata0_r<=d0;
    odata1_r<=d1;
    odata2_r<=d2;
    odata3_r<=d3;
    odata4_r<=d4;
    odata5_r<=d5;
    odata6_r<=d6;
    odata7_r<=d7;
   end
 end
 endmodule           

module SHEX (idata,rst,clk,odata);
input [3:0] idata;
input rst,clk;
output [6:0] odata;
reg [6:0] odata_r;
assign odata=odata_r;
always@(posedge clk or negedge rst)
 begin
   if(rst==1'b0)
    begin
     odata_r<=7'd0;
    end
    else
     begin
      case(idata)
       4'd0:odata_r<=7'b1000000;
       4'd1:odata_r<=7'b1111001;
       4'd2:odata_r<=7'b0100100;
       4'd3:odata_r<=7'b0110000;
       4'd4:odata_r<=7'b0011001;
       4'd5:odata_r<=7'b0010010;
       4'd6:odata_r<=7'b0000010;
       4'd7:odata_r<=7'b1111000;
       4'd8:odata_r<=7'b0000000;
       4'd9:odata_r<=7'b0010000;
       default:odata_r<=7'b0111111;
      endcase
     end
 end
 endmodule

七、工程获取

链接:https://pan.baidu.com/s/1-5V5b6h9lPAKPwkta77wPw?pwd=JWQL
提取码:JWQL
–来自百度网盘超级会员V5的分享
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Doris架构包含哪些技术?为什么进行技术整合?

Doris主要整合了Google Mesa(数据模型)&#xff0c;Apache Impala(MPP Query Engine)和Apache ORCFile (存储格式&#xff0c;编码和压缩)的技术。 为什么要将这三种技术整合? Mesa可以满足我们许多存储需求的需求&#xff0c;但是Mesa本身不提供SQL查询引擎。 Impala是一个…

在做debrief评论自动往上滚动和对讲自动滚动时遇到的问题

描述&#xff1a;在获取滚动高度并且给dom元素设置scrollTo属性后&#xff0c;对于第一个元素的滚动是有问题的&#xff0c;不管在top给补充高度都是无效的。 原因&#xff1a;第一个需要滚动的元素,没有进行正常滚动&#xff1a;因为vue还没有计算出来第一个元素的完整高度&a…

电子器件系列43:IGBT技术参数

以IXBH16N170为例 IGBT/三极管/MOS管的区别&#xff1f; IGBT/三极管/MOS管的区别_哔哩哔哩_bilibili 导通电阻&#xff1f; 如果器件导通&#xff0c;开始通过电流&#xff08;比如100A&#xff09;&#xff0c;那么其对应的电阻就是导通电阻&#xff0c;如果导通电阻小&am…

ONLYOFFICE 7.4版新增函数详解

ONLYOFFICE升级到7.4版本后&#xff0c;在其表格编辑器中新增了一些公式函数&#xff1a;SEQUENCE、XMATCH、EXPAND、FILTER、ARRAYTOTEXT和SORT&#xff0c;大大加强了对矩阵、数组的操作处理能力。这一组函数公式&#xff0c;在微软的Excel里面是专属于Office365和OfficeOnli…

libevent(15)bufferevent filter的过滤器及例子

一、bufferevent filter简单说明 filter之类的东西&#xff0c;相信有php框架或java springboot经验的程序员应该很熟悉&#xff0c;就是加载在输入流之前或输出流之后的一个处理器&#xff0c;用来从业务处分离开来做一些额外的事情。 &#xff08;1&#xff09;读取&#xf…

vscode配置终端默认为git bash

vscode配置终端默认为git bash 文章目录 vscode配置终端默认为git bashctrl shift p 打开设置添加 git bash 并设为默认终端 ctrl shift p 打开设置 添加 git bash 并设为默认终端 注意不要配置 git-bash.exe 的路径。 如果配置为 git-bash.exe 路径&#xff0c;则会单独打…

阿里云服务器ping不通如何解决?

阿里云服务器ping不通&#xff1f;什么原因&#xff1f;在安全组中允许【全部 ICMP(IPv4)】&#xff0c;当然阿里云服务器禁ping也是通过配置安全组的ICMP规则来实现的&#xff0c;阿里云服务器网来详细说下安全组开通ping功能教程&#xff1a; 目录 阿里云服务器ping不通的解…

Node Sass version 6.0.1 is incompatible with ^4.0.0问题解决

运行环境背景 操作系统&#xff1a;Windows 10 64位 node版本&#xff1a;v16.19.1 npm版本&#xff1a;8.19.3 解决方案 卸载当前版本的node-sass和node-loader npm uninstall node-sass npm uninstall node-loader 重新安装指定版本 npm i node-sass6.0.1 node-loade…

计算机图形学综述(一)

计算机已经成为快速、经济地生成图片的强大工具。实际上已经没有哪个领域不能从使用图形显示中获益&#xff0c;因此也就不奇怪为什么计算机图形学的应用是那么广泛。虽然早期的工程和科学上的应用必须依赖于昂贵而笨重的设备&#xff0c;但是计算机技术的发展已经将交互式计算…

sharding-jdbc分片功能学习笔记

sharding-jdbc是sharding-sphere下的一个模块&#xff0c;可以理解为增强版的jdbc&#xff0c;用于解决分库分表、读写分离问题&#xff0c;本文基于4.1.1版本进行说明数据分片功能的流程 该版本提供了如下功能 1.数据分片&#xff08;包括强制路由功能&#xff09; 2.读写分…

win10系统下安装qt5.12.0软件

一、软件下载 1、Qt开源社区下载 下载地址&#xff1a;https://download.qt.io/archive/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe 社区地址&#xff1a; Index of /archive/qt/5.12/5.12.10 2、百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1Sqi…

举例说明什么是卷积计算

卷积计算是一种数学运算&#xff0c;主要用于信号处理、图像处理等领域。它涉及到两个函数&#xff08;通常称为信号和核&#xff09;的互相关运算。在图像处理中&#xff0c;卷积计算通常用于实现图像的平滑、锐化、边缘检测等操作。 以图像处理为例&#xff0c;我们可以用一个…

【面试题24】MySQL如何给一个1000万的表安全的加字段

文章目录 一、前言二、MySQL表添加字段的方案2.1 ALTER TABLE 添加字段2.2 Online Alter Table2.3 Percona Toolkit2.4 给表新增字段2.5 使用触发器添加字段 总结 一、前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP面试专区。 计划将全覆盖PHP开发领域所有的面试题&#xf…

【AIGC】18、MobileSAM | 首个专为移动端设计的更快的 SAM

文章目录 一、背景二、方法2.1 耦合蒸馏2.2 从半蒸馏到解耦蒸馏 三、效果 论文&#xff1a;FASTER SEGMENT ANYTHING: TOWARDS LIGHTWEIGHT SAM FOR MOBILE APPLICATIONS 代码&#xff1a;https://github.com/ChaoningZhang/MobileSAM 出处&#xff1a;韩国庆熙大学 时间&am…

MP4视频格式和mp4v2的移植

目录 1、视频文件 2、MP4 3、MP4学习方法 4、MP4文件格式解析 5、MP4Info工具使用 6、mp4v2移植和播放 6.1、下载mp4v2 6.2、配置并编译 6.3、部署 6.4、编译sample 6.5、准备TF卡 6.6、运行和测试 7、MP4打包源码解析 8.添加网络telnet调试 8.1、为什么添加teln…

java进程注入

本文重点java Instrumentation java Instrumentation指的是可以用独立于应用程序之外的代理&#xff08;agent&#xff09;程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态&#xff0c;替换和修改类定义等。简单一句话概括下&#xff1a;…

11 通信的基本概念

目录 通信分类概览 串行通讯与并行通讯 全双工、半双工及单工通讯 同步通讯与异步通讯 通讯速率 注意 通信分类概览 串行通讯与并行通讯 串行通讯是指设备之间通过少量数据信号线(一般是 8 根以下)&#xff0c;地线以及控制信号线&#xff0c;按数据位形式一位一位地传输…

Windows下创建进程的理解

创建windows进程&#xff0c;需要考虑两个点&#xff0c;即session和权限问题。了解这两点&#xff0c;网络上服务创建界面进程&#xff0c;管理员权限进程创建普通权限进程的代码则很好理解。 1、基础知识 (1) session (2) 权限 CreateProcessAsUser需要传入一个token&#x…

LeetCode 打卡day45--完全背包问题之最小填充次数

一个人的朝圣 — LeetCode打卡第45天 知识总结Leetcode 70. 爬楼梯题目说明代码说明 Leetcode 322. 零钱兑换题目说明代码说明 Leetcode 279. 完全平方数题目说明代码说明 知识总结 今天的问题都可以归结一句话, 在完全背包的问题设置下, 问将该背包填满最少需要放几件物品. L…

java基础(并发编程)-异步模式之生产者/消费者

一、定义要点 与前面的保护性暂停中的GuardedObject不同&#xff0c;不需要产生结果和消费结果的线程一一对应消费队列可以用来平衡生产和消费的线程资源生产者仅负责产生结果数据&#xff0c;不关心数据该如何处理&#xff0c;而消费者专心处理结果数据消息队列是有容量限制的…