
简介基于FPGA实现的BiSS-C接口协议接收模块工程面向需要从光栅尺、编码器等绝对位置传感器读取数据的开发者解决高速同步串行接口的时序解析与数据接收问题。模块按照BiSS-C单向主从模式编写主接口控制时序光栅侧作为从接口本设计完成接收端逻辑并附带仿真代码已通过ModelSim仿真验证可在Xilinx ISE工程中直接打开使用或迁移到其他平台。压缩包共58个文件包括Verilog源文件、ISE工程配置、仿真测试脚本、综合报告、网表文件和仿真波形数据等包体仅358KB结构紧凑。目前已累计2389人学习适合具备一定FPGA基础、正在研究工业总线协议或位置反馈系统的读者。通过该资源可以掌握BiSS-C接收状态机的设计思路、仿真激励的搭建方法、工程编译与波形查看的完整流程还可参考其中包含的日志与报告快速定位常见问题对实际项目开发具有参考价值。 搞运动控制的工程师十有八九都跟编码器打过交道。光电的、磁的、正余弦的接口五花八门增量A/B/Z、绝对式SSI、EnDat、BiSS、HIPERFACE……项目一催大家第一反应就是买专用芯片或者现成模块。其实用FPGA写一个BISS-C接收模块没那么悬核心就三件事时序产生、数据采样、CRC校验。这篇文章把我最近在Xilinx Artix-7上实现的一套BISS-C接口接收模块完整拆开讲一遍从协议细节到RTL结构再到仿真和排查问题的方法适合正在做编码器接口、运动控制或者准备入门FPGA通信协议的同学参考。这套模块解决什么问题简单说就是让FPGA直接跟BISS-C协议的总线式绝对值编码器对话按时钟把位置数据读回来CRC校验通过之后送给CPU比如常见搭配STM32H743走FMC或留在FPGA内部做闭环控制。代码用Verilog写成逻辑资源占用很小高云、安路的器件基本不用改就能移植。下面直接进入正题。1. 项目整体思路与BISS-C协议拆解1.1 为什么用FPGA写BISS-C接收模块先聊点实在的。BISS-C是iC-Haus提出的一种双向同步串行接口协议本质上是SSI协议的增强版多了CRC校验、可变数据位宽和寄存器模式。很多MCU也有硬件BISS-C外设但用起来有几个痛点一是数据位宽和CRC多项式未必可配二是MCU中断响应有抖动读高速编码器时容易丢帧三是很多项目是FPGA做伺服环路、DSP/MCU做上层逻辑位置数据本身就要进FPGA再用MCU转一手纯属多此一举。FPGA的优势在于两点。第一采样时刻完全可控。BISS-C的SLO线是在MA时钟下降沿之后才更新的主站必须在上沿附近采样FPGA可以用基准时钟精确卡住采样窗口不受中断影响。第二协议逻辑和业务逻辑放同一个器件里位置数据读回来直接参与插补、闭环、报警处理延迟低到微秒级以下。我用一个不到2000 LUT的模块就完成了40位帧的接收和CRC校验性价比非常高。1.2 帧结构和时序采样点到底放在哪BISS-C的物理层通常是一根差分MA主站时钟输出和一根差分SLO从站数据返回RS422电平线上空闲时MA为高电平。一次完整的传输过程可以拆成几个阶段我画了一幅文字时序图方便大家理解空闲MA保持高电平SLO保持高电平或由从站拉高。握手主站把MA拉低从站检测到MA下降沿后把SLO拉低作为应答ACK。数据位主站开始输出MA时钟脉冲从站在每个MA周期内依次移出数据。第一位是固定为0的起始位Start之后是位置数据位常见32位MSB在前可选二进制或格雷码再往后是错误位Error和警告位Warning最后是6位CRC。结束主站停止时钟并把MA拉高SLO释放回到高电平。采样时间的把握是整个模块成败的关键。从站的SLO是在MA下降沿附近翻转的存在一个参数叫Td从MA边沿到SLO稳定的延迟手册上通常给几十纳秒到几百纳秒。主站如果直接在MA上升沿打一拍采样很可能采到边沿附近的亚稳态。我实际用的策略是在MA为高电平的中间位置采样也就是上升沿之后再过半个MA周期此时SLO早就稳定了余量最大。后面章节会讲具体怎么用计数器实现这个中点采样。2. FPGA模块架构与时钟采样策略2.1 顶层模块划分三块逻辑各管各的这个接收模块整体上比想象中简单顶层例化之后核心逻辑只有三块。第一块是MA时钟生成器负责把输入的基准时钟分频成编码器要求的MA频率并生成采样使能脉冲第二块是接收状态机负责握手、移位、收帧、校验状态流转第三块是数据输出逻辑把收到的数据和状态打包成寄存器或FIFO读接口。顶层模块端口大致是这样module bissc_rx #( parameter DATA_WIDTH 32, parameter CLK_DIV 20, // MA频率 CLK / CLK_DIV parameter USE_EDS 1 // 是否存在Error/Warning位 )( input wire clk, // 基准时钟100MHz input wire rst_n, input wire read_req, // 触发一次读取 output reg ma_out, // MA时钟输出 input wire slo_in, // SLO数据输入 output reg frame_valid, // 数据有效标志 output reg [47:0] frame_data // 打包后的帧数据 );设计上把MA分频和业务时钟分开是有讲究的。BISS-C协议允许的MA频率最高能到10MHz甚至更高但编码器手册通常会给出一个稳妥值比如5MHz。基准时钟100MHzMA周期就是20个基准时钟这个整数关系非常重要。如果基准时钟不是MA频率的整数倍采样位置会来回漂移数据收得就不稳。选型时先看编码器支持的MA频率再反推基准时钟能整除是最理想的。2.2 MA时钟生成与中点采样策略MA时钟生成器用计数器实现每计数到CLK_DIV/2就翻转一次MA同时产生一个采样脉冲。采样脉冲的位置放在MA高电平的中间也就是MA拉高之后再过CLK_DIV/4个基准时钟。刚才那个例子CLK_DIV20MA高电平持续10个基准时钟那么采样点就在MA上升沿后第5个时钟处。实际代码片段如下reg [5:0] clk_cnt; reg [5:0] sample_cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin clk_cnt 0; ma_out 1b1; end else if (state RUN) begin if (clk_cnt CLK_DIV/2 - 1) begin ma_out ~ma_out; clk_cnt 0; end else begin clk_cnt clk_cnt 1; end end end采样使能信号不能直接用MA高电平判断来生成因为MA在FPGA内部是寄存器输出经过引脚和外部驱动器之后有一个回环延迟SLO线上的数据相对内部时钟是有相移的。更可靠的做法是在基准时钟域里用采样计数器生成一个单周期的sample_en在sample_en为高时把slo_in打一拍进来。这一个寄存器可以同时解决时序约束和亚稳态问题。我最开始图省事直接在外部把SLO和MA接到一起判断边沿结果毛刺一堆后来改成内部计数采样才消停。3. 核心逻辑实现状态机、CRC与数据接口3.1 状态机设计从IDLE到DONE接收状态机的核心是确保握手成功之后才开始移位收数据。状态定义如下IDLE等待read_req此时MA保持高电平。WAIT_ACK把MA拉低等待SLO上出现低电平应答。这里要加超时计数如果几百个时钟周期内没等到ACK说明编码器没接好或者从站没上电要能自动回到IDLE并报错。SHIFTMA正常输出时钟在每个采样点上把SLO移入移位寄存器同时计数。DONE一帧收完拉高frame_valid把结果锁存MA拉高释放总线。WAIT_ACK这个状态特别容易翻车。有些编码器在MA拉低之后要等好几个微秒才响应如果超时设得太短就会误报。我按照编码器手册建议的典型响应时间把超时值放宽到手册上限的两倍实测各种编码器都稳了。状态机主体大概长这样always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; frame_valid 1b0; end else begin case (state) IDLE: begin frame_valid 1b0; ma_out 1b1; if (read_req) state WAIT_ACK; end WAIT_ACK: begin ma_out 1b0; if (!slo_in) begin bit_cnt 0; state SHIFT; end else if (ack_cnt ACK_TIMEOUT) begin state IDLE; // 上报超时错误 end end SHIFT: begin // 移位逻辑见下方 end DONE: begin frame_valid 1b1; state IDLE; end endcase end endSHIFT状态里面每个MA高电平的中点产生一次采样使能把slo_in推进shift_reg。数据位宽可以配置从起始位开始一共收DATA_WIDTHUSE_EDS*26位收满之后把有效数据位、错误位、警告位和CRC位分开存放方便上层使用。3.2 CRC-6校验与格雷码转换BISS-C的CRC多项式是x^6 x^1 x^0也就是常见的CRC-6初值通常是0。计算方式是边收边算每收到一位数据和当前CRC寄存器的最高位异或再反馈到最低位和次低位。校验范围是从起始位之后第一个数据位开始一直到CRC位之前的所有位CRC本身不算进去。用LFSR实现的Verilog代码如下function automatic [5:0] crc6_next; input bit data_in; input bit [5:0] crc_cur; begin crc6_next[0] crc_cur[5] ^ data_in; crc6_next[1] crc_cur[0] ^ crc_cur[5] ^ data_in; crc6_next[2] crc_cur[1]; crc6_next[3] crc_cur[2]; crc6_next[4] crc_cur[3]; crc6_next[5] crc_cur[4]; end endfunction收到最后6位CRC之后把计算得到的CRC和接收到的CRC逐位比较。如果不等置位错误标志这一帧数据直接丢弃不要拿错误的位置数据去参与控制。这里有一点必须提醒市面上BISS-C编码器有些支持格雷码输出有些支持二进制输出如果选的是格雷码FPGA里收完要做一次格雷转二进制。转换逻辑很简单高位保留从高到低逐位异或即可但别忘了在位宽改变时同步调整。3.3 数据输出给MCU寄存器接口和FIFO数据收回来之后总要交给上层。如果FPGA是协处理器后面接了STM32H743这样的MCU用FMC接口是最常见也最高效的方案。FMC本质上就是一个并行SRAM接口FPGA端实现一组寄存器映射MCU用地址读写寄存器就行。接收模块这边我建议至少留三个寄存器状态寄存器读取忙/超时错误/CRC错误、数据低32位寄存器、数据高16位寄存器包含Error、Warning和CRC原值。如果MCU和FPGA之间通信频率不高或者数据量大、时序要求不高也可以走SPI或UART。但我个人还是推荐FMC因为MCU读位置数据是一个高频操作SPI一帧几十个字节读一次要几百微秒放在伺服周期里完全不可接受。FMC读32位寄存器只要一个访问周期效率高一个数量级。另外提一句如果FPGA本身是主控位置数据要直接参与闭环那就没必要绕寄存器了直接把frame_data接进位置环模块即可模块内部再并行扩展一个FIFO专门给调试用方便用逻辑分析仪抓波形。4. 踩坑记录、排查技巧与仿真验证4.1 时序约束与亚稳态处理FPGA里做通信协议时序约束绕不开。首先是SLO输入信号必须加set_input_delay约束告诉综合器信号相对MA的延迟范围。测试时我用过编码器手册给的Td典型值再留出足够裕量综合报告里SLO的采集时序基本都能满足。如果不加约束综合器默认输入信号和时钟同沿布局布线可能把采样寄存器放在时序很差的位置板子上跑起来就随机出错。再一个是SLO线在进FPGA之前要经过差分接收芯片比如AM26LV32转成单端芯片的传播延迟也要算进输入延迟里。我一开始没把这块算进去导致约束和实际差了几十纳秒高MA频率时CRC总是随机错。后来把接收芯片的延迟加上问题就消失了。亚稳态处理也很关键。SLO虽然是同步信号但毕竟来自编码器经过线缆和接收芯片后相对FPGA内部时钟还是有相位差。我在采样寄存器后面又加了一级同步寄存器让数据先打两拍再进状态机这样即使第一拍采到亚稳态第二拍也能稳定下来状态机逻辑不会收到不确定电平。占用两个寄存器换来的稳定性非常值。4.2 常见问题速查表下面这张表是我在这类调试里反复遇到的现象和排查方法基本覆盖了新手会踩的大部分坑现象可能原因排查方法SLO一直为高进不了WAIT_ACK差分线接反、从站没供电、接收芯片方向错误示波器抓SLO波形检查SLO/SLO-极性确认编码器供电握手正常但CRC总错采样点位置不对、约束没加、MA频率过高降低MA频率测试检查采样使能位置核对CRC计算范围数据偶发跳变SLO信号毛刺、地线干扰、亚稳态加同步寄存器检查PCB走线SLO上并0.1uF电容读回来的位置一直是0数据位宽配置和编码器不一致确认编码器实际位数检查起始位是否被当成数据高MA频率下完全不通线缆太长、驱动能力不足、接收芯片带宽不够降频验证排除时序问题检查线缆类型和长度有一个我印象特别深的案例某次在客户现场编码器数据偶尔跳一个很大的值CR C偶尔报错。排查了半天最后发现是编码器线缆跟电机动力线绑在一起走线电机一加速SLO线上就叠了一大片干扰。后来把编码器线单独走问题立刻消失。所以电气设计和软件排查一样重要别一上来就怀疑代码。4.3 仿真验证先造一个虚拟编码器上板调试之前强烈建议先在Vivado/ModelSim里跑一遍仿真。我习惯的做法是用testbench模拟编码器行为生成一个符合BISS-C时序的SLO信号里面随机放位置数据算好CRC然后喂给接收模块对比收到的数据和原始数据是否一致。这样能在纯逻辑层面把状态机、CRC、采样逻辑的问题提前过滤掉省下大量上板时间。testbench里最关键的是要模拟SLO在MA下降沿之后Td才变化的真实行为。很多人写仿真时让SLO和MA边沿完全对齐结果板上时序稍有偏移就收不到数据代码本身却没有问题。我仿真时会故意加上50到100纳秒的延时让接收模块的采样逻辑真的踩到稳定区这样仿真通过了上板基本就稳了。再补充一个小技巧仿真时写一个自检断言每收完一帧就自动比对位置数据和CRC一旦不一致立刻打印错误并stop跑完10000帧随机数据大概率能把隐藏的边界问题逼出来。我在这个项目的早期版本就是靠这种随机化仿真发现CRC计算少算了一位的问题如果直接上板看波形真不一定能这么快定位。最后再分享一个建议如果你用的是Vivado仿真通过之后记得在硬件上接逻辑分析仪ILA把MA、SLO、状态机的几个关键状态拉出来触发条件设为frame_valid上升沿抓到一帧完整波形后跟协议手册的时序图对照一遍确认每个边沿都对齐。这一步走完整个接收模块就算真正落地了。本文还有配套的精品资源点击获取