基于H7的曼彻斯特编码/解码/串口系统设计

描述

前言

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

方案讲解

关于曼彻斯特编码

曼彻斯特编码(Manchester coding),又称自同步码、相位编码(phase encoding,PE),能够用信号的变化来保持发送设备和接收设备之间的同步。

值得一提的是,曼彻斯特编码有两种截然相反的约定:

它用电压的变化来分辨0和1,从高电平到低电平的跳变代表1,而从低电平到高电平的跳变代表0(G.E.Tomas编码方式)。

从高电平到低电平的跳变代表0,而从低电平到高电平的跳变代表1(IEEE 802.3编码方式),信号的保持不会超过一个比特位的时间间隔。即使是0或1的序列,信号也将在每个时间间隔的中间发生跳变。这种跳变将允许接收设备的时钟与发送设备的时钟保持一致。

两种曼彻斯特编码:

FPGA

优点:

与 NRZ 相比,曼彻斯特编码提供一种同步机制,保证发送端与接收端信号同步。

缺点:

曼彻斯特编码的频率要比 NRZ 高一倍,传输等量数据所需的带宽大一倍。

编码:

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

FPGA

解码:

曼彻斯特译码电路设计的目的,是如何准确地从曼彻斯特码的码流中提取出“10”和“01”信号,并将其转换成普通二进制编码中的“0”和“1”。

在实际设计电路中,可以采用一个缓存器,保存上一个时钟采集到的信号和当前时钟采集到的信号,当缓存器的内容是“01”时,输出“1”;当缓存器的内容是“10”时,输出“0”;当缓存器的内容是“00”或“11”时,输出维持不变。

FPGA

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

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

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

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

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

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

任务&实验效果

用两块 FPGA 开发板设计一个曼彻斯特码编码和解码系统;

第1块板负责在按键后将拨码开关拨出的8位二进制码用曼彻斯特码发出;

第2块板负责在收到曼彻斯特码号将其解析并在数码管上显示。两块板记得共地。(也可以用 debugware 进行波形读取)

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

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

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

硬件选择

1、H7P20N0L176-M2H1

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

FPGA

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

FPGA

2、XC7A35TFGG484-2

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

FPGA

程序设计

1、顶层模块

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

1 //************Message***************//
2 //技术交流:bumianzhe@126.com
3 //关注CSDN博主:“千歌叹尽执夏”
4 //Author: 千歌叹尽执夏
5 //All rights reserved                          
6 //-------------------------------
7 // Target Devices: H7P20N0L176-M2H1
8 // Tool Versions:  Fuxi 2023.1
9 // File name:     hme_manchester
10 // Last modified Date:  2023年6月27日2000
11 // Last Version:        V1.1
12 // Descriptions:        曼彻斯特解码&编码&串口
13 //-------------------------------------
14 //***********************************//
15
16 module hme_manchester(
17        input        sys_clk,    //高频时钟,可以是50MHz,本次入20MHz
18        input        rst_n,
19       input        rx_d,   //接收到的bit数据(本实验支持回环)
20        output       tx_d,     //将tx_data进行编码并以串口方式发送出去
21        //发送使能
22        input        tx_en_n,//发送使能,使用按键控制
23        input [7:0]  tx_data,//发送的8bits数据,拨码开关控制
24        output          sh_cp,  //串行数据输出
25        output          st_cp,     //移位寄存器的时钟输出
26        output          ds         //存储寄存器的时钟输出
27 );
28
29 wire [7:0] rx_data;
30 wire       rx_valid;
31 reg  [7:0] rx_data_dis;             //最终显示的接收数据
32 wire [7:0] sel;                    //数码管位选(选择当前要显示的数码管)
33 wire [6:0] seg;                    //数码管段选(当前要显示的内容)
34
35 pll_v1 u_pll_v1(
36     .clkin0        (sys_clk),        //输入20MHz时钟
37    .locked        (),
38    .clkout0    (clk)            //输出50MHz时钟
39 );
40
41
42
43 manchester_tx u_manchester_tx_0(
44        .clk(clk),                //高频时钟,可以是50MHz
45        .rst_n(rst_n),
46      //.tx_data(tx_data),      //待发送的字节数据
47        .tx_data(8'b11110000),  //由于板卡上没有拨码开关,这里直接输入数据进行模拟
48        .tx_en(!tx_en_n),       //发送使能
49        .tx_ready(),
50        .tx_d(tx_d)
51        );
52 //如果没有两块板卡的,可以将manchester_tx的tx_d信号接到manchester_rx的rx_d
53 //形成回环实验,并利用debugware进行数据查看。
54 manchester_rx u_manchester_rx_0(
55        .clk(clk),                 //高频时钟,可以是50MHz
56        .rst_n(rst_n),
57        .rx_d(rx_d),               //接收到的bit数据
58      //.rx_d(tx_d),               //回环实验
59        .rx_data(rx_data),
60        .rx_valid(rx_valid)
61        );
62       
63 always@(posedge clk or negedge rst_n)
64 begin
65 if(!rst_n)
66    rx_data_dis <= 8'd0;
67 else
68    rx_data_dis <= rx_valid?rx_data:rx_data_dis;

69 end

70

71

72 HC595_driver u_HC595_driver(
73        .clk(clk),
74        .reset_n(rst_n),
75        .data({1'd0,seg,sel}),
76        .s_en(1'b1),
77        .sh_cp(sh_cp),
78        .st_cp(st_cp),
79        .ds(ds)
80    );
81
82 Hex8 u_Hex8(
83        .clk(clk),
84        .reset_n(rst_n),
85        .en(1'b1),       

86 .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]}),
87        .sel(sel),
88        .seg(seg)
89    );
90 //使用debugware查看编码&解码波形
91 debugware_v2_1 u_debugware_v2_1(
92    .trig_out_0        (),
93    .data_in_0       

94 ({tx_en_n,tx_d,rx_data}),
    .ref_clk_0        (clk)
95 );
96
97 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。

1 //************Message***************//
2 //技术交流:bumianzhe@126.com
3 //关注CSDN博主:“千歌叹尽执夏”
4 //Author: 千歌叹尽执夏
5 //All rights reserved                          
6 //-------------------------------
7 // Target Devices: H7P20N0L176-M2H1
8 // Tool Versions:  Fuxi 2023.1
9 // File name:     manchester_tx
10 // Last modified Date:  2023年6月17日900
11 // Last Version:        V1.1
12 // Descriptions   编码&发送模块模块

13 //-------------------------------------
14 //***********************************//
15 module manchester_tx(

16 input       clk,    //高频时钟,可以是50MHz

17        input       rst_n,

18        input [7:0] tx_data,//待发送的字节数据

19        input       tx_en,  //发送使能

20        output      tx_ready,

21        output      tx_d

22        );

23        parameter IDLE = 1'b0;//空闲状态

24        parameter TXS  = 1'b1;//发送状态

25        reg tx_ready_r;

26        reg tx_d_r;

27        reg state;

28        reg [4:0] tx_cnt;//发送的bits计数器

29        reg [7:0] tx_data_reg;

30     wire clk_bps_en;

31   assign tx_ready = tx_ready_r;

32   assign tx_d = tx_d_r;
33   precise_divider//分频模块

34   #(

35   //DEVIDE_CNT = 85.89934592 * fo  @50M

36   //DEVIDE_CNT = 42.94967296 * fo  @100M

37    .DEVIDE_CNT(32'd1649267)//9600Hz * 2

38  )u_precise_divider_0

39  (

40   //global clock

41   .clk(clk),

42  .rst_n(rst_n),

43
44     //user interface

45     //.divide_clk()

46     .divide_clken(clk_bps_en)

47      );

48      always@(posedge clk or negedge rst_n)

49       begin

50      if(rst_n == 1'b0)

51        begin

52     state <= IDLE;

53     tx_ready_r <= 1'b1;

54     tx_d_r <= 1'b0;
55      tx_data_reg <= 8'd0;

56      tx_cnt <= 5'd0;

57      end
58      else if(clk_bps_en)

59      begin

60      case(state)

61      IDLE:begin

62       state <= tx_en?TXS:IDLE;

63       tx_data_reg <= tx_en?tx_data:tx_data_reg;

64      tx_ready_r <= tx_en?1'b0:1'b1;

65      tx_cnt <= 5'd0;

66      end

67     TXS:begin
68     tx_cnt <= (tx_cnt >= 5'd19)?5'd0:tx_cnt+1'b1;

69    state <= (tx_cnt >= 5'd19)?IDLE:TXS;

70    tx_ready_r <= (tx_cnt >= 5'd19)?1'b1:1'b0;

71    if(tx_cnt[4:1] == 4'd0)

72   tx_d_r <= 1'b1;//发2个高电平

73      else if(tx_cnt[4:1] <= 4'd8)

74       begin

75       //如果tx_data_reg[7]的值为1,则将tx_cnt[0]的值赋给tx_d_r;

76      //否则将tx_cnt[0]的逻辑反值(即0变成1,1变成0)赋给tx_d_r。

77       tx_d_r <= tx_data_reg[7]?tx_cnt[0]:!tx_cnt[0];//1--01 0--10

78        tx_data_reg <= tx_cnt[0]?(tx_data_reg<<1):tx_data_reg;

79       end

80    else begin

81        tx_d_r <= 1'b0;

82      end

83     end

84     endcase

85     end     

86 end

87 endmodule
88

3、解码&接收模块

接收到的串行数据进行解串行化,然后进行解码。

解码与编码原理差不多,这里不做赘述。

1 //*********** Message **********//
2 //技术交流:bumianzhe@126.com
3 //关注CSDN博主:“千歌叹尽执夏”
4 //Author: 千歌叹尽执夏
5 //All rights reserved                             
6 //----------------------------------------
7 // Target Devices:         H7P20N0L176-M2H1
8 // Tool Versions:       Fuxi 2023.1
9 // File name:           manchester_rx
10 // Last modified Date:  2023年6月18日1500
11 // Last Version:        V1.1
12 // Descriptions:        解码&接收模块
13 //--------------------------------------------------------
14 //********************************************************************//
15 module manchester_rx(
16        input        clk,    //高频时钟,可以是50MHz
17        input        rst_n,
18        input        rx_d,   //接收到的bit数据
19        output [7:0] rx_data,
20        output       rx_valid
21 );
22       
23    parameter IDLE = 1'b0;//空闲状态
24    parameter RXS  = 1'b1;//接收状态
25    reg state;
26    reg rx_valid_r0,rx_valid_r;
27    reg [15:0] rx_data_reg;//缓存16bits的rx数据,每2bits代表1bit数据//01:1 10:0
28    reg [7:0]  rx_data_r;
29    wire [7:0] rx_data_w;
30    wire clk_bps_en;
31    reg [3:0] rx_cnt;//对16倍波特率的时钟计数
32    reg [3:0] byte_cnt;//对已接收的字节计数
33    assign rx_data  = rx_data_r;
34    assign rx_valid = rx_valid_r;
35   
36    //将一组长度为16的串行数据rx_data_reg转换成一组长度为8的并行数据rx_data_w,
37    //即将接收到的串行数据进行解串行化。
38    generate
30        genvar i;
40        for(i=0;i<8;i=i+1) begin:u1
41            assign rx_data_w[i] = !rx_data_reg[i*2+1] && rx_data_reg[i*2];
42        end
43    endgenerate
44   
45    precise_divider//分频模块
46    #(
47        //DEVIDE_CNT = 85.89934592 * fo  @50M
48        //DEVIDE_CNT = 42.94967296 * fo  @100M
49     .DEVIDE_CNT(32'd13194139)    //9600Hz * 16
50    )u_precise_divider_0
51    (
52        //global clock
53        .clk(clk),
54        .rst_n(rst_n),
55       
56        //user interface
57        //.divide_clk()
58        .divide_clken(clk_bps_en)
59    );
60    
61    always@(posedge clk or negedge rst_n)
62    begin
63    if(rst_n == 1'b0)
64        begin
65        rx_data_r <= 8'd0;
66        rx_valid_r <= 1'b0;
67        end
68    else
69        begin
70        rx_data_r <= rx_data_w;
71        rx_valid_r <= rx_valid_r0;
72        end
73    end
74    always@(posedge clk or negedge rst_n)
75    begin
76    if(rst_n == 1'b0)
77        begin
78        state <= IDLE;
79        rx_data_reg <= 16'd0;
80       
81        rx_cnt <= 4'd0;
82        byte_cnt <= 4'd0;
83        rx_valid_r0 <= 1'b0;
84        end
85    else if(clk_bps_en)
86        begin    
87        case(state)
88        IDLE:begin
89            rx_cnt      <= rx_d?(rx_cnt >= 4'd10)?4'd0:rx_cnt+1'b1:
90                        4'd0
91                        ;
92            state       <= (rx_cnt >= 4'd10)?RXS:IDLE;
93            rx_data_reg <= 16'd0;
94            byte_cnt    <= 4'd0;
95            rx_valid_r0 <= 1'b0;
96        end
97        RXS:begin
98            rx_cnt <= (rx_cnt >= 4'd7)?4'd0:rx_cnt+1'b1;
99            if(rx_cnt >= 4'd7)
100                rx_data_reg <= {rx_data_reg[14:0],rx_d};
101            else
102                rx_data_reg <= rx_data_reg;
103            byte_cnt <= (rx_cnt >= 4'd7)?byte_cnt+1'b1:byte_cnt;
104            state <= ((rx_cnt >= 4'd7) && byte_cnt == 4'd15)?IDLE:RXS;
105            rx_valid_r0 <= ((rx_cnt >= 4'd7) && byte_cnt == 4'd15);
106        end
107        endcase
108        end
109    end
110 endmodule
111

4、HC595驱动模块

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

1 //************Message ***************//
2 //技术交流:bumianzhe@126.com
3 //关注CSDN博主:“千歌叹尽执夏”
4 //Author: 千歌叹尽执夏

5 //All rights reserved  

6 //---------------------------------------------------------

7 // Target Devices:         H7P20N0L176-M2H1
8 // Tool Versions:       Fuxi 2023.1
9 // File name:           HC595_driver
10 // Last modified Date:  2023年6月11日2000
11 // Last Version:        V1.1
12 // Descriptions:        驱动HC595,发出数据和选通信号
13 //--------------------------------------------------------
14//*******************************************************************************//
15 module HC595_driver(
16    clk,    
17    reset_n,
18    data,
19    s_en,
20
21    sh_cp,
22    st_cp,
23    ds
24 );
25
26    input clk;
27    input reset_n;
28    input [15:0]data;
29    input s_en;
30   
31    output reg sh_cp;
32    output reg st_cp;
33    output reg ds;
34   
35    assign reset=~reset_n;
36    parameter CNT_MAX = 2;
37   
38    reg [15:0]r_data;
39    always@(posedge clk)
40    if(s_en)
41        r_data <= data;
42
43    reg [7:0]divider_cnt;//分频计数器;
44   
45    always@(posedge clk or posedge reset)
46    if(reset)
47        divider_cnt <= 0;
48    else if(divider_cnt == CNT_MAX - 1'b1)
49        divider_cnt <= 0;
50    else
51        divider_cnt <= divider_cnt + 1'b1;
52       
53    wire sck_plus;
54    assign sck_plus = (divider_cnt == CNT_MAX - 1'b1);
55      
56    reg [5:0]SHCP_EDGE_CNT;
57   
58    always@(posedge clk or posedge reset)
59    if(reset)
60        SHCP_EDGE_CNT <= 0;
61    else if(sck_plus)begin
62        if(SHCP_EDGE_CNT == 6'd32)
63            SHCP_EDGE_CNT <= 0;
64        else
65            SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
66    end
67    else
68        SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
69       
70    always@(posedge clk or posedge reset)
71    if(reset)begin
72        st_cp <= 1'b0;
73        ds <= 1'b0;
74        sh_cp <= 1'd0;
75    end
76    else begin
77        case(SHCP_EDGE_CNT)
78    0: begin sh_cp <= 0; st_cp <= 1'd0;ds <= r_data[15];end
79     1: begin sh_cp <= 1; st_cp <= 1'd0;end
80     2: begin sh_cp <= 0; ds <= r_data[14];end
81     3: begin sh_cp <= 1; end
82     4: begin sh_cp <= 0; ds <= r_data[13];end    
83     5: begin sh_cp <= 1; end
84     6: begin sh_cp <= 0; ds <= r_data[12];end    
85     7: begin sh_cp <= 1; end
86     8: begin sh_cp <= 0; ds <= r_data[11];end    
87     9: begin sh_cp <= 1; end
88    10: begin sh_cp <= 0; ds <= r_data[10];end    
89    11: begin sh_cp <= 1; end
90    12: begin sh_cp <= 0; ds <= r_data[9];end    
91    13: begin sh_cp <= 1; end
92    14: begin sh_cp <= 0; ds <= r_data[8];end    
93    15: begin sh_cp <= 1; end
94    16: begin sh_cp <= 0; ds <= r_data[7];end    
95    17: begin sh_cp <= 1; end
96    18: begin sh_cp <= 0; ds <= r_data[6];end    
97    19: begin sh_cp <= 1; end
98    20: begin sh_cp <= 0; ds <= r_data[5];end    
99    21: begin sh_cp <= 1; end
100   22: begin sh_cp <= 0; ds <= r_data[4];end    
101   23: begin sh_cp <= 1; end
102   24: begin sh_cp <= 0; ds <= r_data[3];end    
103   25: begin sh_cp <= 1; end
104   26: begin sh_cp <= 0; ds <= r_data[2];end    
105   27: begin sh_cp <= 1; end
106   28: begin sh_cp <= 0; ds <= r_data[1];end            
107   29: begin sh_cp <= 1; end
108   30: begin sh_cp <= 0; ds <= r_data[0];end
109   31: begin sh_cp <= 1; end
110   32: st_cp <= 1'd1;
111            default:        
112               begin
113                    st_cp <= 1'b0;
114                   ds <= 1'b0;
115                    sh_cp <= 1'd0;
116                end
117        endcase
118    end
119
120 endmodule
121

5、段选&位选模块

FPGA

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

比较简单的数码管段选、位选模块,这里不做赘述。

1 //************* Message *************//
2 //技术交流:bumianzhe@126.com
3 //关注CSDN博主:“千歌叹尽执夏”
4 //Author: 千歌叹尽执夏
5 //All rights reserved  

6 //---------------------------------------------------------
7 // Target Devices:         H7P20N0L176-M2H1
8 // Tool Versions:       Fuxi 2023.1
9 // File name:           Hex8
10 // Last modified Date:  2023年6月11日1300
11 // Last Version:        V1.1
12 // Descriptions:        分频信号,段选和位选信号生成,输出需要显示的数据、段选和位选值
13 //--------------------------------------------------------
14//****************************************************************************************//
15 module Hex8(
16    clk,
17    reset_n,
18    en,
19    disp_data,
20    sel,
21    seg
22);
23    assign reset=~reset_n;
24    input clk;    //50M
25    input reset_n;
26    input en;    //数码管显示使能,1使能,0关闭
27   
28    input [31:0]disp_data;
29   
30    output [7:0] sel;//数码管位选(选择当前要显示的数码管)
31    output reg [6:0] seg;//数码管段选(当前要显示的内容)
32   
33    reg [14:0]divider_cnt;//25000-1
34   
35    reg clk_1K;
36    reg [7:0]sel_r;
37   
38    reg [3:0]data_tmp;//数据缓存
39
40//    分频计数器计数模块
41    always@(posedge clk or posedge reset)
42    if(reset)
43        divider_cnt <= 15'd0;
44    else if(!en)
45        divider_cnt <= 15'd0;
46    else if(divider_cnt == 24999)
47        divider_cnt <= 15'd0;
48    else
49        divider_cnt <= divider_cnt + 1'b1;
50
51 //1K扫描时钟生成模块        
52    always@(posedge clk or posedge reset)
53    if(reset)
54        clk_1K <= 1'b0;
55    else if(divider_cnt == 24999)
56        clk_1K <= ~clk_1K;
57    else
58        clk_1K <= clk_1K;
59       
60 //8位循环移位寄存器
61    always@(posedge clk_1K or posedge reset)
62    if(reset)
63        sel_r <= 8'b0000_0001;
64    else if(sel_r == 8'b1000_0000)
65        sel_r <= 8'b0000_0001;
66    else
67        sel_r <=  sel_r << 1;
68       
69    always@(*)
70        case(sel_r)
71            8'b0000_0001:data_tmp = disp_data[3:0];
72            8'b0000_0010:data_tmp = disp_data[7:4];
73            8'b0000_0100:data_tmp = disp_data[11:8];
74            8'b0000_1000:data_tmp = disp_data[15:12];
75            8'b0001_0000:data_tmp = disp_data[19:16];
76            8'b0010_0000:data_tmp = disp_data[23:20];
77            8'b0100_0000:data_tmp = disp_data[27:24];
78            8'b1000_0000:data_tmp = disp_data[31:28];
79            default:data_tmp = 4'b0000;
80        endcase
81       
82    always@(*)
83        case(data_tmp)
84            4'h0:seg = 7'b1000000;
85            4'h1:seg = 7'b1111001;
86            4'h2:seg = 7'b0100100;
87            4'h3:seg = 7'b0110000;
88            4'h4:seg = 7'b0011001;
89            4'h5:seg = 7'b0010010;
90            4'h6:seg = 7'b0000010;
91            4'h7:seg = 7'b1111000;
92            4'h8:seg = 7'b0000000;
93            4'h9:seg = 7'b0010000;
94        endcase
95       
96    assign sel = (en)?sel_r:8'b0000_0000;
97
98 endmodule
99

五debugware 回环实验

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

1 //如果没有两块板卡的,可以将 manchester_tx 的 tx_d 信号接到 manchester_rx 的 rx_d

2 //形成回环实验,并利用debugware进行数据查看。

3 manchester_rx u_manchester_rx_0(

4    .clk(clk),     //高频时钟,可以是50MHz
5     .rst_n(rst_n),               
6     //.rx_d(rx_d),   //接收到的bit数据7     .rx_d(tx_d)//回环实验

8     .rx_data(rx_data),

9     .rx_valid(rx_valid)

10    );

例化一个 debugware IP:

FPGA

1 //使用debugware查看编码&解码波形

2 debugware_v2_1 u_debugware_v2_1(

3       .trig_out_0(),

4       .data_in_0({tx_en_n,tx_d,rx_data_dis}),//数据拼接

5         .ref_clk_0(clk)

6    );

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

FPGA

兼容设计

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

FPGA

1 module hme_manchester(
2        //其他信号-略
3        //数码管
4        output [6:0] odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7
5 );
6 //其他模块-略-自行复制补充
7
8 HEX HEX_u(
9 .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]}),
10 .rst(1'b1),
11 .clk(clk),
12 .odata0(odata0),
13 .odata1(odata1),
14 .odata2(odata2),
15 .odata3(odata3),
16 .odata4(odata4),
17 .odata5(odata5),
18 .odata6(odata6),
19 .odata7(odata7)
20 );    
21 endmodule

1 module HEX(idata,rst,clk,odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7);
2 input [31:0] idata ;
3 input clk,rst;
4 output [6:0] odata0,odata1,odata2,odata3,odata4,odata5,odata6,odata7;
5 wire [6:0] d0,d1,d2,d3,d4,d5,d6,d7;
6 reg [6:0] odata0_r,odata1_r,odata2_r,odata3_r,odata4_r,odata5_r,odata6_r,odata7_r;
7
8 assign odata0=odata0_r;
9 assign odata1=odata1_r;
10 assign odata2=odata2_r;
11 assign odata3=odata3_r;
12 assign odata4=odata4_r;
13 assign odata5=odata5_r;
14 assign odata6=odata6_r;
15 assign odata7=odata7_r;
16 SHEX SHEX0 (.idata(idata[3:0]),
17            .rst(rst),
18            .clk(clk),
19            .odata(d0));
20 SHEX SHEX1 (.idata(idata[7:4]),
21            .rst(rst),
22            .clk(clk),
23            .odata(d1));
24 SHEX SHEX2 (.idata(idata[11:8]),
25            .rst(rst),
26            .clk(clk),
27            .odata(d2));
28 SHEX SHEX3 (.idata(idata[15:12]),
29            .rst(rst),
30            .clk(clk),
31            .odata(d3));
32 SHEX SHEX4 (.idata(idata[19:16]),
33            .rst(rst),
34            .clk(clk),
35            .odata(d4));
36 SHEX SHEX5 (.idata(idata[23:20]),
37            .rst(rst),
38           .clk(clk),
39            .odata(d5));
40 SHEX SHEX6 (.idata(idata[27:24]),
41            .rst(rst),
42            .clk(clk),
43            .odata(d6));
44 SHEX SHEX7 (.idata(idata[31:28]),
45            .rst(rst),
46            .clk(clk),
47            .odata(d7));
48 always@(posedge clk or negedge rst)
49 begin
50  if(rst==1'b0)
51   begin
52    odata0_r<=7'd0;
53    odata1_r<=7'd0;
54    odata2_r<=7'd0;
55    odata3_r<=7'd0;
56    odata4_r<=7'd0;
57    odata5_r<=7'd0;
58    odata6_r<=7'd0;
59    odata7_r<=7'd0;
60   end
61   else
62   begin
63    odata0_r<=d0;
64    odata1_r<=d1;
65    odata2_r<=d2;
66    odata3_r<=d3;
67    odata4_r<=d4;
68    odata5_r<=d5;
69    odata6_r<=d6;
70    odata7_r<=d7;
71   end
72 end
73 endmodule           
74

1 module SHEX (idata,rst,clk,odata);
2 input [3:0] idata;
3 input rst,clk;
4 output [6:0] odata;
5 reg [6:0] odata_r;
6 assign odata=odata_r;
7 always@(posedge clk or negedge rst)
8 begin
9   if(rst==1'b0)
10    begin
11     odata_r<=7'd0;
12    end
13    else
14     begin
15      case(idata)
16       4'd0:odata_r<=7'b1000000;
17       4'd1:odata_r<=7'b1111001;
18       4'd2:odata_r<=7'b0100100;

19       4'd3:odata_r<=7'b0110000;
20       4'd4:odata_r<=7'b0011001;
21       4'd5:odata_r<=7'b0010010;
22       4'd6:odata_r<=7'b0000010;
23       4'd7:odata_r<=7'b1111000;
24       4'd8:odata_r<=7'b0000000;
25       4'd9:odata_r<=7'b0010000;
26      default:odata_r<=7'b0111111;
27      endcase
28     end
29 end
30 endmodule
31

 

 

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分