尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

FPGA实现曼彻斯特编码与解码:从原理到Verilog代码实践

FPGA实现曼彻斯特编码与解码:从原理到Verilog代码实践 1. 从串口通信的痛点说起为什么需要曼彻斯特编码最近在调试一个低速的板间通信链路用的是最基础的UART串口。按理说波特率匹配、起始位停止位正确数据就该稳稳当当地传过来。但实际跑起来隔三差五就丢一两个字节或者收到一堆乱码。用示波器一抓波形问题就暴露了在长距离或者有干扰的线上信号边沿变得不那么陡峭接收端采样时钟的微小偏移就可能导致把一位“1”错判成“0”或者反过来。这种问题根源在于普通的NRZ不归零编码比如UART用的它的时钟信息是隐含的。发送方和接收方必须事先约定一个精确的波特率并且在整个通信过程中保持严格的同步。一旦线路环境变差或者时钟源有漂移同步就很容易丢失。这时候就需要一种自带“时钟”的编码方式让接收方能从数据流本身恢复出同步时钟这就是曼彻斯特编码要解决的核心问题。你可能在以太网10BASE-T、RFID如Mifare卡、一些工业总线如Profibus里都听过它。它的魅力在于每一位数据都包含一个电平跳变。这个强制性的跳变就像给数据流打上了节拍器接收端可以据此来校准自己的采样时钟极大地提升了在恶劣信道下的抗干扰能力和时钟恢复的鲁棒性。今天我们就抛开理论直接用Verilog在FPGA里实现一个曼彻斯特编码器和解码器把原理焊死在代码里。2. 曼彻斯特编码的核心跳变即信息在动手写代码前我们必须把曼彻斯特编码的规则吃透这里有两个关键点编码规则和时钟关系。2.1 两种约定IEEE与G.E. Thomas曼彻斯特编码有两种主流定义用之前必须明确否则编解码会对不上IEEE 802.3以太网标准在位元中间发生跳变。0编码为低电平跳变到高电平上升沿1编码为高电平跳变到低电平下降沿。G.E. Thomas 标准与IEEE相反。0编码为高电平跳变到低电平下降沿1编码为低电平跳变到高电平上升沿。为了后续讨论方便我们采用IEEE 802.3标准。记住这个核心每一位的正中间必须有一次电平跳变。这个跳变的方向携带了数据信息。2.2 编码时钟与数据速率的关系这是实现时最容易搞错的地方。假设我们的原始数据速率比特率是1 Mbps即每位数据持续1 μs。 在曼彻斯特编码下为了在每位中间产生一次跳变编码后的信号变化速率即波特率必须是原始数据速率的两倍。也就是说编码器需要一个频率为2 MHz的时钟来驱动。我们可以这样理解原始数据位被这个2倍速时钟“细分”成了两个时隙前半段和后半段。编码规则决定了这两个时隙的电平组合。对于数据0IEEE标准前半段时隙为低电平。中间发生跳变。后半段时隙为高电平。因此编码后的一个比特位实际上由两个更窄的“码元”组成。接收端需要以这个2倍速的节奏来采样才能准确识别出中间的跳变沿和前后电平进而解码出原始数据。注意有些资料或实现中可能会使用更高倍数的时钟如4倍、8倍或16倍数据速率来产生更精细的编码波形或辅助解码但2倍频是理论上的最低要求。我们这里采用最直接的2倍频方案逻辑最清晰。3. Verilog编码器实现用状态机“雕刻”波形有了理论基础我们开始设计编码器。输入是原始数据位流和对应的时钟输出是曼彻斯特编码后的波形。最清晰、最可靠的设计方法是使用一个有限状态机FSM。3.1 模块接口与状态定义首先定义模块接口。我们需要clk系统主时钟频率是原始数据速率的2倍。rst_n异步低电平复位。data_in输入的待编码数据位。data_in_valid指示data_in有效的脉冲信号。当它为高时表示当前data_in上的数据位需要被编码并输出。manchester_out曼彻斯特编码输出。状态机需要记录当前正在输出一个数据位的哪个部分。我们定义两个状态IDLE空闲状态等待有效数据输入。BIT_PHASE_FIRST正在输出一个数据位的前半段。BIT_PHASE_SECOND正在输出一个数据位的后半段。实际上IDLE可以视为一个特殊的等待状态。更精简的设计可以只用两个状态代表前后半段并用一个计数器控制切换。但为了逻辑更直观我们采用三个状态。module manchester_encoder ( input wire clk, // 2倍数据速率的时钟 input wire rst_n, // 异步复位低有效 input wire data_in, // 输入数据位 input wire data_in_valid, // 输入数据有效信号 output reg manchester_out // 曼彻斯特编码输出 ); // 状态定义 localparam STATE_IDLE 2b00; localparam STATE_BIT_PHASE_FIRST 2b01; localparam STATE_BIT_PHASE_SECOND 2b10; reg [1:0] current_state, next_state; reg data_latch; // 锁存住data_in_valid有效时刻的数据防止在输出过程中data_in变化3.2 状态转移与输出逻辑状态机的核心是每当data_in_valid有效就启动一个完整的比特输出周期。这个周期持续两个clk周期因为clk是2倍速。进入BIT_PHASE_FIRST根据锁存的数据data_latch和IEEE规则决定第一个半位的电平。一个clk周期后无条件转移到BIT_PHASE_SECOND输出第二个半位的电平与第一个半位相反。再一个clk周期后检查是否有新的data_in_valid。如果有则锁存新数据并进入下一个比特的BIT_PHASE_FIRST如果没有则回到IDLE。输出逻辑是组合逻辑直接根据当前状态和锁存的数据决定// 状态寄存器更新 always (posedge clk or negedge rst_n) begin if (!rst_n) begin current_state STATE_IDLE; data_latch 1b0; end else begin current_state next_state; // 在IDLE状态且有效信号来临时锁存数据 if ((current_state STATE_IDLE) data_in_valid) begin data_latch data_in; end // 也可以在状态转移时锁存但上述方式更清晰 end end // 下一状态逻辑 always (*) begin next_state current_state; // 默认保持当前状态 case (current_state) STATE_IDLE: begin if (data_in_valid) begin next_state STATE_BIT_PHASE_FIRST; end end STATE_BIT_PHASE_FIRST: begin // 经过一个时钟周期必然进入后半段 next_state STATE_BIT_PHASE_SECOND; end STATE_BIT_PHASE_SECOND: begin // 后半段结束判断是否有新数据 if (data_in_valid) begin next_state STATE_BIT_PHASE_FIRST; // 连续数据直接开始下一个比特 end else begin next_state STATE_IDLE; // 无数据回到空闲 end end default: next_state STATE_IDLE; endcase end // 输出逻辑 (曼彻斯特编码 IEEE 802.3) always (*) begin case (current_state) STATE_IDLE: begin // 空闲时输出可以是一个固定电平这里选择输出0。根据实际接口要求也可定义为高阻或其他。 manchester_out 1b0; end STATE_BIT_PHASE_FIRST: begin // 比特前半段数据0输出0数据1输出1 (因为中间跳变后半段会相反) manchester_out data_latch; // 对于IEEE1的前半段是高 end STATE_BIT_PHASE_SECOND: begin // 比特后半段总是与前半段相反 manchester_out ~data_latch; end default: manchester_out 1b0; endcase end endmodule关键点解析data_latch的必要性在输出一个完整比特的两个阶段期间输入数据data_in可能已经改变。我们必须锁存data_in_valid有效时刻的数据确保整个比特编码的一致性。输出逻辑是IEEE标准对于数据1data_latch1。前半段(FIRST)输出1高电平后半段(SECOND)输出0低电平在位元中间形成了一个下降沿。符合IEEE对1的定义。状态机驱动使用状态机而非简单计数器逻辑更清晰易于扩展例如未来处理帧间隔、前导码等。BIT_PHASE_SECOND结束后对data_in_valid的判断实现了数据的“背靠背”连续传输提高了效率。3.3 仿真测试与波形分析写个简单的Testbench验证一下。我们假设数据速率是1Mbps那么编码时钟clk就是2MHz。我们发送一个序列101100。timescale 1ns / 1ps module tb_manchester_encoder(); reg clk; reg rst_n; reg data_in; reg data_in_valid; wire manchester_out; manchester_encoder uut ( .clk(clk), .rst_n(rst_n), .data_in(data_in), .data_in_valid(data_in_valid), .manchester_out(manchester_out) ); // 生成2MHz时钟 (周期500ns) always #250 clk ~clk; initial begin // 初始化 clk 0; rst_n 0; data_in 0; data_in_valid 0; #1000; rst_n 1; #500; // 发送数据 1 data_in 1; data_in_valid 1; (posedge clk); // 等待一个时钟沿确保信号被采样 data_in_valid 0; // 等待两个时钟周期一个完整的曼彻斯特位 repeat(2) (posedge clk); // 发送数据 0 data_in 0; data_in_valid 1; (posedge clk); data_in_valid 0; repeat(2) (posedge clk); // 发送数据 1 data_in 1; data_in_valid 1; (posedge clk); data_in_valid 0; repeat(2) (posedge clk); // 发送数据 1 data_in 1; data_in_valid 1; (posedge clk); data_in_valid 0; repeat(2) (posedge clk); // 发送数据 0 data_in 0; data_in_valid 1; (posedge clk); data_in_valid 0; repeat(2) (posedge clk); // 发送数据 0 data_in 0; data_in_valid 1; (posedge clk); data_in_valid 0; repeat(2) (posedge clk); #2000; $finish; end endmodule在仿真波形中你应该看到当data_in1且data_in_valid脉冲后manchester_out在下一个clk周期变高再下一个clk周期变低形成一个“高-低”脉冲对应下降沿在比特中间。当data_in0时manchester_out输出“低-高”脉冲对应上升沿在比特中间。每个数据位恰好占用两个clk周期。实操心得在仿真时除了看波形最好能计算一下manchester_out的上升沿/下降沿与clk边沿之间的时间关系。理想情况下跳变应发生在clk的边沿上同步设计。这能帮你检查时序逻辑是否正确避免产生毛刺。4. Verilog解码器实现从跳变中提取时钟与数据解码是编码的逆过程但难度更大。因为输入信号manchester_in可能含有抖动、毛刺且与本地时钟clk不同步。解码器的目标是从这个异步的、自带时钟信息的信号中恢复出同步时钟recovered_clk和原始数据data_out。这里介绍一种经典且可靠的数字解码方案过采样与边沿检测。我们使用一个比曼彻斯特码元速率即2倍数据速率高得多的本地系统时钟例如16倍来对输入信号进行过采样然后通过数字逻辑判断跳变沿的位置和方向进而恢复出时钟和数据。4.1 过采样与边沿检测原理假设原始数据速率仍是1Mbps曼彻斯特码元速率是2MHz码元宽度是500ns。 我们使用一个16倍于原始数据速率的本地时钟即16MHz周期为62.5ns。用它来采样manchester_in每个曼彻斯特码元会被采样大约8次500ns / 62.5ns 8。解码步骤采样与同步用16MHz时钟对异步的manchester_in进行两级寄存器同步消除亚稳态得到稳定的采样值sync_manchester。边沿检测对sync_manchester进行边沿检测。通过比较当前采样值和前一个时钟周期的采样值可以判断出上升沿、下降沿和无变化。跳变窗口定位曼彻斯特编码规定跳变发生在比特中间。因此当我们检测到一个跳变沿时我们可以推断这个跳变沿很可能位于一个比特位的正中间。跳变沿之前的采样点属于当前比特位的前半段之后的采样点属于后半段。数据判决根据跳变沿的方向上升沿还是下降沿和解码标准IEEE可以判决出这个比特位的值。例如检测到下降沿从高到低且采用IEEE标准则判决当前比特为1。时钟恢复每次在比特中间检测到跳变沿时就产生一个“时钟有效”脉冲。这个脉冲的节奏就是恢复出来的数据时钟1MHz。我们可以用这个脉冲来锁存判决出的数据。4.2 解码器模块设计这个解码器相对复杂我们分块实现。module manchester_decoder ( input wire clk_16x, // 过采样时钟例如16倍数据速率 input wire rst_n, input wire manchester_in, // 曼彻斯特编码输入 output reg data_out, // 解码后的数据输出 output reg data_out_valid, // 解码数据有效脉冲 output reg recovered_clk // 恢复出的时钟占空比不一定为50% ); // 同步链消除亚稳态 reg [1:0] sync_reg; always (posedge clk_16x or negedge rst_n) begin if (!rst_n) begin sync_reg 2b00; end else begin sync_reg {sync_reg[0], manchester_in}; end end wire sync_manchester sync_reg[1]; // 边沿检测 reg sync_manchester_dly; always (posedge clk_16x or negedge rst_n) begin if (!rst_n) begin sync_manchester_dly 1b0; end else begin sync_manchester_dly sync_manchester; end end wire rising_edge_detected (~sync_manchester_dly) sync_manchester; wire falling_edge_detected sync_manchester_dly (~sync_manchester); wire any_edge_detected rising_edge_detected | falling_edge_detected; // 跳变沿计数器与窗口逻辑 // 理想情况下两个跳变沿之间应该间隔8个过采样周期一个曼彻斯特码元宽度。 // 我们用一个计数器在检测到跳变沿后开始计数用来预测下一个跳变沿应该在何时出现。 // 同时这个计数器也帮助我们定位“比特中间”和“比特边界”。 reg [7:0] bit_timer; // 计数器宽度需能计到大于16*2一个完整比特的过采样周期数 reg bit_middle_zone; // 标志位表示当前处于一个比特位的“中间区域” reg bit_start_zone; // 标志位表示当前处于一个比特位的“开始区域”刚经过跳变 always (posedge clk_16x or negedge rst_n) begin if (!rst_n) begin bit_timer 8d0; bit_middle_zone 1b0; bit_start_zone 1b0; end else begin if (any_edge_detected) begin // 检测到任何跳变沿重置计数器并标记为比特开始 bit_timer 8d1; bit_start_zone 1b1; bit_middle_zone 1b0; end else begin if (bit_timer ! 8d0) begin bit_timer bit_timer 8d1; end // 根据计数器值更新区域标志 // 假设一个曼彻斯特码元是8个过采样周期一个完整比特是16个。 // 跳变沿后的0-3个周期认为是比特开始区域不稳定避免采样 // 跳变沿后的4-7个周期认为是比特中间区域稳定用于数据判决不跳变在中间中间区域其实是跳变发生地数据在前后半段稳定 // 实际上数据判决应该在跳变沿之后立即进行根据跳变方向或者在一个比特的稳定区域采样。 // 更常见的做法在预测的“比特中间”位置即跳变沿处进行边沿判决在预测的“比特边界”位置即跳变沿后半个比特周期输出数据。 end // 简化逻辑我们可以在检测到跳变沿时直接根据边沿类型判决数据并延迟半个比特周期8个clk_16x后输出。 end end // 数据判决与恢复时钟生成 reg [3:0] output_delay_counter; reg decoded_bit; reg output_data_valid_pending; always (posedge clk_16x or negedge rst_n) begin if (!rst_n) begin recovered_clk 1b0; data_out 1b0; data_out_valid 1b0; output_delay_counter 4d0; decoded_bit 1b0; output_data_valid_pending 1b0; end else begin // 默认值 data_out_valid 1b0; recovered_clk 1b0; // 当检测到跳变沿时立即进行数据判决并启动输出延迟计数器 if (any_edge_detected) begin // 根据IEEE标准判决 if (falling_edge_detected) begin decoded_bit 1b1; // 下降沿 - 数据1 end else if (rising_edge_detected) begin decoded_bit 1b0; // 上升沿 - 数据0 end // 在跳变沿处产生一个恢复时钟的脉冲可以是一个周期的高电平 recovered_clk 1b1; // 启动延迟计数器半个比特周期后输出有效数据 output_delay_counter 4d8; // 8个过采样周期 半个比特周期 (16/2) output_data_valid_pending 1b1; end else begin recovered_clk 1b0; // 恢复时钟脉冲只持续一个周期 end // 处理输出延迟 if (output_delay_counter ! 4d0) begin output_delay_counter output_delay_counter - 4d1; if (output_delay_counter 4d1) begin // 延迟结束输出锁存的数据 if (output_data_valid_pending) begin data_out decoded_bit; data_out_valid 1b1; output_data_valid_pending 1b0; end end end end end endmodule关键点与难点解析亚稳态处理manchester_in是异步信号必须通过同步器两级或多级触发器再进入核心逻辑这是数字电路与异步信号交互的铁律。边沿检测的准确性rising_edge_detected和falling_edge_detected是组合逻辑在高速时钟下可能产生毛刺。在实际应用中可能需要将边沿检测信号也打一拍再使用或者采用更稳健的边沿检测电路例如检测连续几个采样点的变化。抗抖动与容错上面的简化代码假设信号是理想的。现实中信号可能有抖动导致跳变沿的位置偏离理想点。一个更健壮的实现需要加入“容忍窗口”。例如我们预测跳变沿应该在第8个过采样点附近出现。如果边沿检测发生在第7或第9个点我们也应认为是有效的跳变并以此微调我们的比特定时计数器。这通常涉及一个数字锁相环DPLL或类似的状态机逻辑。数据输出时机为什么要在检测到跳变沿后延迟半个比特周期才输出数据因为跳变沿位于比特中间。当我们检测到跳变沿并判决出数据后这个数据位实际上才刚刚完成了一半。我们需要等待这个比特位完全过去即再经过半个比特周期到达下一个比特的边界再输出这个数据这样在接收端看来数据是在稳定的比特边界上变化的便于后续处理。恢复时钟recovered_clk是一个脉冲信号其上升沿与曼彻斯特码流中的跳变沿对齐即比特中间。这个时钟可以用于驱动后续需要与接收数据同步的模块。注意它的占空比不是50%。4.3 解码器的仿真与挑战为解码器编写Testbench时需要模拟带有时钟偏差和抖动的曼彻斯特输入信号这比编码器测试复杂得多。一个基本的测试思路是用编码器产生的理想信号作为解码器的输入验证其能否正确恢复数据。// 在Testbench中实例化编码器和解码器 manchester_encoder encoder( .clk(clk_2m), .rst_n(rst_n), .data_in(tb_data_in), .data_in_valid(tb_data_valid), .manchester_out(manchester_signal) ); manchester_decoder decoder( .clk_16x(clk_16m), .rst_n(rst_n), .manchester_in(manchester_signal), // 直接连接理想信号 .data_out(decoded_data), .data_out_valid(decoded_valid), .recovered_clk(rec_clk) );在仿真中你需要观察manchester_signal上的跳变沿是否都能被decoder检测到any_edge_detected脉冲。检测到边沿后decoded_bit是否被正确赋值根据边沿类型。延迟半个比特周期后decoded_valid脉冲是否出现同时decoded_data输出是否正确。rec_clk脉冲是否与manchester_signal的跳变沿对齐。踩坑实录解码器对时钟频率比这里是16倍非常敏感。如果这个比例设置得不合适例如刚好是整数倍但有偏差或者输入信号的抖动过大解码错误率会急剧上升。在实际项目中往往需要根据信道特性和数据速率通过仿真和实测来确定最优的过采样倍数和容错窗口参数。一种更高级的做法是使用可变的过采样率或数字锁相环来动态跟踪输入信号的速率。5. 系统集成与实测考量从仿真到板卡将编码器和解码器集成到一个系统中并考虑实际硬件实现时的问题。5.1 顶层系统连接一个典型的自环测试Loopback Test顶层模块如下module manchester_top #( parameter DATA_RATE 1_000_000 // 1 Mbps )( input wire sys_clk, // 高频系统时钟如50MHz input wire rst_n, input wire [7:0] tx_data, // 待发送的字节数据 input wire tx_start, // 发送启动信号 output wire [7:0] rx_data, // 接收到的字节数据 output wire rx_valid, // 接收数据有效 output wire manchester_tx, // 发送的曼彻斯特信号可连接至IO input wire manchester_rx // 接收的曼彻斯特信号可连接至IO ); // 时钟生成从sys_clk产生编码时钟(2*DATA_RATE)和解码过采样时钟(16*DATA_RATE) // 这里简化处理假设时钟频率已满足要求。实际需用PLL或时钟分频器。 wire clk_2m; wire clk_16m; // ... 时钟管理单元实例化 ... // 发送端将并行字节转为串行位流然后进行曼彻斯特编码 reg [7:0] tx_shift_reg; reg [3:0] tx_bit_cnt; reg tx_busy; wire tx_bit; wire tx_bit_valid; // 发送状态机将tx_data按位送出 always (posedge clk_2m or negedge rst_n) begin if (!rst_n) begin tx_busy 1b0; tx_shift_reg 8d0; tx_bit_cnt 4d0; end else begin tx_bit_valid 1b0; if (tx_start !tx_busy) begin tx_busy 1b1; tx_shift_reg tx_data; tx_bit_cnt 4d8; end else if (tx_busy) begin // 每个clk_2m周期送出一位注意clk_2m是曼彻斯特码元速率即2倍数据速率 // 我们需要每两个clk_2m周期一个完整比特周期送出一位原始数据。 // 因此需要一个更低速的使能信号来控制数据位输出速率。 // 此处引入一个分频计数器 end end end assign tx_bit tx_shift_reg[7]; // 发送MSB first // ... 详细的分频和移位逻辑 ... manchester_encoder u_encoder( .clk(clk_2m), .rst_n(rst_n), .data_in(tx_bit), .data_in_valid(tx_bit_valid), // 这个信号应该是1MHz的脉冲 .manchester_out(manchester_tx) ); // 接收端曼彻斯特解码然后将串行位流组装成字节 manchester_decoder u_decoder( .clk_16x(clk_16m), .rst_n(rst_n), .manchester_in(manchester_rx), .data_out(rx_bit), .data_out_valid(rx_bit_valid), // 1MHz的脉冲指示一个有效比特 .recovered_clk() // 可选连接 ); reg [7:0] rx_shift_reg; reg [3:0] rx_bit_cnt; always (posedge clk_16m or negedge rst_n) begin // 注意这里用clk_16m来采样rx_bit_valid if (!rst_n) begin rx_shift_reg 8d0; rx_bit_cnt 4d0; rx_valid 1b0; end else begin rx_valid 1b0; if (rx_bit_valid) begin rx_shift_reg {rx_shift_reg[6:0], rx_bit}; // LSB first 或 MSB first需与发送端一致 rx_bit_cnt rx_bit_cnt 4d1; if (rx_bit_cnt 4d7) begin rx_data {rx_shift_reg[6:0], rx_bit}; // 组装完成一个字节 rx_valid 1b1; rx_bit_cnt 4d0; end end end end endmodule5.2 实际部署的注意事项时钟生成与管理这是系统稳定性的基石。编码器需要的clk_2m和解码器需要的clk_16m必须来自同一个低抖动的时钟源如FPGA上的PLL并且频率要非常精确。任何频率偏差都会导致解码器逐渐失步。IO约束与时序manchester_tx和manchester_rx是FPGA的管脚信号。必须为其添加正确的IO标准约束如LVCMOS33和输出延迟约束确保信号质量。对于manchester_rx输入可能需要配置施密特触发器输入以增强抗噪声能力。信道非理想特性实际PCB走线或电缆会引入衰减、反射和噪声。曼彻斯特编码虽然抗干扰能力强但极端情况下仍可能出错。可以考虑增加前导码在真实数据前发送一段固定的同步模式如连续的“01”交替让解码器的时钟恢复电路提前锁定相位。添加帧校验如CRC校验在字节或帧级别进行错误检测发现错误可请求重传。模拟前端对于长距离或恶劣环境可能需要驱动器和接收器芯片来增强信号。资源与性能权衡过采样倍数越高抗抖动能力越强但消耗的FPGA资源寄存器、逻辑也越多并且对系统时钟频率要求越高。需要根据数据速率和FPGA性能找到一个平衡点。对于更高速度的曼彻斯特解码可能会采用专用的高速串行接口如SERDES来实现过采样。6. 进阶话题从标准曼彻斯特到差分曼彻斯特我们上面实现的是标准曼彻斯特编码。还有一种变体叫差分曼彻斯特编码。它的规则是每位开始时都有一个电平跳变这与标准曼彻斯特的位中间跳变不同。位中间是否跳变取决于数据值0表示在位中间有跳变1表示在位中间无跳变。差分曼彻斯特编码的优点在于因为每位开始都有跳变时钟恢复更容易同步。同时它的解码不依赖于绝对的电平高低只依赖于相邻跳变之间的间隔因此抗共模干扰能力更强。许多令牌环网络和某些RFID标准使用它。用Verilog实现差分曼彻斯特编码器/解码器其思路与标准曼彻斯特类似但状态机需要记录前一个比特结束时的电平状态以决定当前比特开始是否需要跳变实际上开始总是跳变所以重点是记录当前电平并根据数据值决定位中间是否插入跳变。解码器则需要检测位开始跳变和位中间跳变或有無来判决数据。这将是另一个有趣的练习其核心依然是状态机和精确的定时控制。通过这个从理论到代码从仿真到考量的完整过程我们不仅实现了一个曼彻斯特编解码器更深入理解了同步通信中时钟恢复这一核心挑战的解决思路。下次当你的UART在嘈杂环境中失灵时或许可以想想是不是该给它换一颗“曼彻斯特”的心脏了。
返回列表