ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C#实战:打造轻量级Gerber查看器,解析RS-274X与图形渲染

C#实战:打造轻量级Gerber查看器,解析RS-274X与图形渲染 简介一款基于C#实现的Gerber查看器源码包面向PCB设计、电子制造与CAM工艺人员用于读取并可视化Gerber文件中的导线、焊盘、丝印等层信息解决文件预览与后期检查需求。压缩包共35个文件体积仅76KB主体为11个.cs源文件辅以项目配置csproj/sln、窗体与界面资源resx/resources、运行配置config及exe以及PDB调试符号等可直接在Visual Studio中打开GMDO_V4.0项目进行编译与二次开发。目前已有3627人访问学习适合具备基础C#知识、想深入理解Gerber解析流程或计划开发CAM Viewer的开发者。资源完整包含Form1窗体逻辑、文件列表管理、程序入口及属性设置代码结构清晰能直观演示如何按行解析指令、建立图形对象并通过WinForms/WPF渲染显示为自研PCB查看工具提供了灵活可扩展的基础框架。1. 为什么我要用 C# 写一个 Gerber 查看器先交代背景。前几年做 PCB 相关项目时频繁要在工厂、供应商、同事之间来回确认生产文件。Gerber 文件是 PCB 行业的通用“语言”但问题在于大多数桌面端 EDA 工具AD、PADS、Allegro体积大、启动慢、还吃授权而 Gerber 查看器这块要么是商业付费软件功能冗余要么是网页版上传文件存在保密顾虑。于是自己动手用 C# 写一个轻量 Gerber 查看器的念头就冒出来了。这个项目的定位不是替代 Cam350 这类专业工具而是解决日常最核心的三个需求快速打开 Gerber 文件确认图层内容、坐标、外形和拼板信息不需要完整版 EDA 软件就能给结构、硬件、厂家快速截图并标注离线和隐私安全文件不出电脑不用上传任何第三方服务器。在技术选型上我最终选定 C# 加 WinForms后来也搬到 WPF 验证过目标框架 .NET 6/8。原因很简单C# 处理文本解析和图形绘制的效率足够高GDI / System.Drawing 对 2D 位图和矢量绘制支持完善而且 Windows 环境下部署方便一个 exe 带个配置文件就能跑连安装包都省了。如果你是做硬件、嵌入式、PCB Layout 或者 PCBA 工厂端工艺的这个项目可以直接套用如果你是想入门 C# 文件解析和图形渲染的开发者这个项目的难度曲线也很友好——核心不在算法而在于把格式规则吃透、把边界情况处理好。2. Gerber 格式到底在说什么RS-274X 核心规则先聊格式本身。Gerber 文件本质上是文本文件每一行就是一条绘图指令由不同的字母开头的命令字构成。最常用的是 RS-274X 标准也叫 Extended Gerber。如果你打开一个 .gbr 文件看到的可能是这样的内容%FSLAX26Y26*% %MOMM*% %ADD10C,0.200*% %ADD11R,1.500X0.250*% %LPD*% D10* X10000Y20000D02* X30000Y20000D01* M02*这就是一个完整的小文件。它干了这些事设置坐标格式为绝对坐标、6 位整数 6 位小数单位是毫米定义了第 10 号光圈为圆形直径 0.2mm第 11 号光圈为矩形 1.5x0.25mm然后移动笔、画一条线、结束。解析 Gerber 有几个关键点必须吃透坐标格式FSLAX26Y26 表示 X 和 Y 都是 2 位整数加 6 位小数。实际坐标值需要根据这个精度做换算比如 X10000 在当前格式下实际是 10mm。单位的坑MOMM 是公制MOIN 是英制。很多文件在毫米和英寸之间转换出问题就是忘了读取单位指令。光圈定义ADD 指令定义 D 码对应的形状常见形状有圆形C、矩形R、方形S、热焊盘O和自定义轮廓。不同厂家的导出器支持范围不一样有些非标准形状需要做兼容。绘图命令D01 落笔画线D02 抬笔移动D03 曝光/画焊盘。这个三个指令几乎覆盖了所有图形绘制的基本动作。圆弧插补G74 是单象限圆弧模式G75 是多象限圆弧模式。圆弧不是直接给圆心坐标而是通过终点相对起点的偏移量IJ和方向来确定解析时必须算清楚象限和圆心位置。对这些指令有一个整体认知之后再看代码实现就不会迷路。很多人一开始就扎进代码写正则结果遇到 G74/G75 圆弧就乱掉本质上是格式规则没吃透。3. 查看器的整体架构解析、模型与渲染三层分离代码层面我把整个项目分成三层解析层、数据模型层、渲染层。这样做的好处是后续可扩展性极强比如如果你想增加导出位图、导出 DXF、甚至做拼板预览只需要在对应层加方法就行。数据模型层先定义几个核心类public class GerberFile { public FileFormat Format { get; set; } public ListAperture Apertures { get; set; } new(); public ListGerberLayer Layers { get; set; } new(); public BoundingBox Bounds { get; set; } } public class Aperture { public int Code { get; set; } public string Shape { get; set; } public double Width { get; set; } public double Height { get; set; } public double HoleDiameter { get; set; } } public class ReportItem { public int X { get; set; } public int Y { get; set; } public bool IsExposure { get; set; } }GerberFile 对应一个完整的图层集文件Aperture 对应光圈定义ReportItem 则是一个相对原始的“绘图动作记录”。渲染时把这些动作逐条读出来即可在画布上绘制。解析层的核心思路不是一次性把所有内容读进内存再处理而是逐行流式解析。对于大文件一份顶层铜箔 Gerber 可能有几十 MB流式解析可以很好地控制内存占用。状态机是这个实现里最关键的部分因为 Gerber 指令虽然以行为单位但命令字可以连在一起写比如上面的例子中每一行末尾是星号而且某些指令会被连续输出。状态机需要维护几个核心状态当前光圈号Current D-Code、当前坐标CurrentX / CurrentY、笔状态Pen Down / Up、当前单位、当前坐标格式、当前圆弧模式。每次读取到新指令时更新对应状态并输出绘图动作。遇到未知指令时先缓存起来并打印警告而不是直接崩溃这是兼容各种厂家导出文件的关键。渲染层用 System.Drawing.Graphics把模型层的数据按照视图窗口和缩放比例绘制到 PictureBox 或自绘控件上。这里有一个很实用的设计先渲染到位图缓存再显示到屏幕。因为 Gerber 文件可能包含几十万条绘图指令如果直接在 Paint 事件里逐条画拖动窗口和缩放时会卡到无法接受。缓存位图配合局部重绘体验好很多。架构确定之后真正的开发难点在细节处理下面逐块说。4. 解析器与渲染过程的实操实现4.1 用状态机吃透指令序列我建议解析器不要用正则一家独大而是用“读取指令 状态更新”的方式。核心循环大致这样while ((line reader.ReadLine()) ! null) { line line.Trim(); if (string.IsNullOrEmpty(line)) continue; // 有些文件一行里会连续出现多个星号结束的指令 var parts line.Split(*, StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts) { ProcessCommand(part); } }ProcessCommand 里根据首字母分流处理例如遇到 FS 就更新坐标格式遇到 MO 就更新单位遇到 ADD 就新增光圈遇到 D** 就切换当前光圈遇到 X/Y 就更新坐标并触发绘图动作。这个结构很朴素但非常稳健因为 Gerber 指令是流式的天然适合状态机。坐标处理这里有一个必须提的细节Gerber 中坐标字符串可能是 X00000Y00000D02 连写的也可能指令和坐标混在一个字段里比如 G75X123Y456D02。所以解析时不能简单按固定位置截取正确的做法是顺序扫描字符串中的每个字母X、Y、I、J、D、G、M用字母作为分隔边界提取数字部分。这也是我从实际项目里踩出来的坑——第一版用正则去匹配 XY 数字遇到 X69 和 Y420 之间的边界时经常出错后来改成扫描法之后再没出过问题。4.2 圆弧计算的细节圆弧计算是 Gerber 解析里最容易被忽略的大坑。G74/G75 决定象限模式IJ 给出的是圆心相对于起点的偏移量而非绝对坐标。计算圆弧时我先根据当前模式算出圆心坐标再算出起始角和终止角然后把圆弧细分成多段直线来逼近绘制。// 单象限模式下圆心相对于起点求偏移 double centerX startX iOffset; double centerY startY jOffset; double startAngle Math.Atan2(startY - centerY, startX - centerX); double endAngle Math.Atan2(endY - centerY, endX - centerX);这里需要特别注意角度方向。Gerber 坐标体系里默认是逆时针为正但实际文件里经常会出现顺逆方向与预期相反的情况所以最好把圆弧旋转方向也做成可配置项。另外单象限模式时圆弧角度不能超过 90 度如果起始角和终止角的差大于 90 度往往是解析错误而不是源文件问题需要回到坐标精度和 IJ 偏移的换算上排查。4.3 渲染层的三件套缩放、平移、量测渲染层除了画图还要支持几个常规操作放大、缩小、适应窗口、十字光标读取坐标、测量两点距离。我用的方案是维护一个视图变换结构体public class ViewTransform { public double ScaleX { get; set; } 1.0; public double ScaleY { get; set; } 1.0; public double OffsetX { get; set; } 0; public double OffsetY { get; set; } 0; public PointF WorldToScreen(double x, double y) { return new PointF((float)(x * ScaleX OffsetX), (float)(-y * ScaleY OffsetY)); } }画布坐标系把 Y 轴反转这样 PCB 的物理方向和屏幕显示保持一致。缩放中心应该以鼠标当前位置为基准而不是固定以左上角为锚点否则放大时视口会漂移这是很影响体验的细节。实现上其实就是先记录鼠标位置的物理坐标然后更新变换参数最后把该物理坐标映射回原屏幕位置保证鼠标指向的点在缩放前后位置不变。图层颜色管理也值得花点心思。顶层铜、底层铜、丝印、阻焊、钻孔层用不同颜色区分。我提供了一套预设配色顶层铜红色、底层铜蓝色、丝印白色、阻焊绿色、钻孔黑色。这样光看颜色就能立刻判断是什么层。半透明叠加更是刚需在检查顶层和底层是否对齐时把两层叠在一起用透明色显示非常直观。System.Drawing 里 Color.FromArgb(alpha, r, g, b) 可以直接实现性能上只要不是叠加太多层完全能接受。4.4 从 AD/PADS/Allegro 导出的文件该怎么适配实际工作中Gerber 文件来源五花八门。Altium Designer 默认导出的扩展名是 .GTL、.GBL、.GTO、.GBO 等PADS 是 .phoAllegro 是 .art。虽然格式标准都是 RS-274X但各家在细节上埋了不少变量Altium 导出时通常会有 .GPI 文件记录光圈列表不过标准 RS-274X 已经把光圈定义写进 Gerber 主文件里了所以不需要额外解析外部光圈文件PADS 导出时默认把文件名截断为 8.3 格式而且小数点分隔符可能与系统区域设置有关Allegro 导出时对精度设置非常敏感经常有设计文件用了最高精度比如 5 位整数 5 位小数而自研查看器没适配到这么多位就会导致坐标全部错位。我的结论是解析器对坐标格式的位数要尽量宽容不要写死只支持 2:6 或者 2:5最好做成动态读取 FSLA 指令里三位数字并且在处理时对齐到同样的幂次。否则同一个文件在不同精度设置下导出的结果会天差地别。5. 常见问题与排查技巧实录项目做到中后期我顺手整理了一个踩坑清单这些坑在网上的博客里几乎找不到但每一个都真实耗费过大量时间。5.1 坐标整体偏移或者镜像翻转如果你打开文件之后发现图形整体往一个方向飘了几毫米十有八九是坐标格式解析不对。比如源文件是 2:5 格式2 位整数 5 位小数但你的解析器按 2:6 去读取那么 X12345 就会被当成 0.12345 而不是 1.2345整体缩放比不对。镜像翻转则要检查坐标是否应该做 X 或 Y 轴取反这通常和导出时的镜像层设置有关比如顶层视图和底层视图从底层视角看自然就是镜像的。5.2 圆弧变成直角或椭圆变形圆弧变形最常见的两个原因一是 IJ 偏移的精度丢失二是在 G74 单象限模式下把跨象限的圆弧强行按单段直线绘制。前者要检查坐标格式解析是否正确后者需要按象限拆分圆弧。还有一次遇到圆弧变成了椭圆查了半天是 X 和 Y 方向的缩放比例不一致最终发现作图控件宽高比没设置成 1:1真凶反而在渲染层。5.3 孔径Aperture形状匹配错乱当文件里出现大量 ADD 指令而且 D 码跨段使用时不同段用同一个 D 码定义成不同形状如果没有在解析时实时映射 D 码与形状就会出现圆形焊盘画成矩形、矩形画成热焊盘的问题。解决办法是每次读取 ADD 指令时立即更新 DCode 映射表而不是等到绘图指令时才去查旧映射。另一个细节是某些导出器会把 D00 到 D09 保留给特殊用途自定义形状往往从 D10 开始这从 AD 和 Allegro 导出的文件里经常遇到。5.4 大文件的性能优化一份复杂的多层板 Gerber 全部打开后绘图指令量以百万计如果仍然逐条在 Paint 事件里调用 Graphics.DrawLine那么每次重绘都可能卡顿。我用两级处理解决先将渲染结果绘制到内存中的大位图缩放或平移时先对位图做变换等用户停止交互 300 毫秒后再重新渲染高精度缓存。再加上 .NET 提供的 BufferedGraphics交互流畅度立刻提升一个档次。5.5 单位显示混乱有的文件同时使用英制线宽和公制板框或者单位指令放置在不同位置。解析时不要只读取文件开头的一个 MO 指令而要在每次需要用到单位的地方检查当前状态。视图层显示尺寸时做成跟随当前文件单位切换显示并提供一个下拉框在毫米/英寸之间切换这样既能满足国内客户习惯又能兼容国际厂家的需求。6. 一些值得留存的实操经验和扩展方向这个查看器在第一版可用之后我陆续给组件做过几个轻量扩展这里说两个比较有价值的。第一个是 “PNG 快照导出”。给结构工程师看拼板信息、给供应商发生产问题时一张带坐标基准尺寸的截图比一堆文件传起来直观得多。实现思路也不复杂创建一个大位图把当前所有可见图层按设定颜色渲染上去再在边缘用 Graphics.DrawString 标上文件名称、缩放比例、单位、关键坐标点。这个功能在内部沟通中非常高频基本成了每个版本都保留的固定功能。第二个是 “极简 BOM 定位辅助”。Gerber 文件本身没有元件位号信息但你可以通过解析钻孔层文件Excellon 格式或者读取坐标文件比如 AD 导出的 Pick and Place 文件把位号和 xy 坐标关联起来。之后点击位号就能在画布上定位到对应焊盘位置。这个功能算不上什么高科技但配合 Board Fabrication 的审查流程非常方便。在.NET 版本升级过程中我还试过用 SkiaSharp 替代 System.Drawing 做渲染后端。坦白说如果只是做基本的 Gerber 预览System.Drawing 完全够用但如果你有后续的跨平台计划比如把查看器搬到 Linux 或者嵌入到 Web 服务里做自动化检查那么从一开始就选定 SkiaSharp 或者 ImageSharp 作为渲染抽象会更省事。这个取舍没有标准答案取决于项目边界和使用团队的习惯。最后再分享一个我在实际项目中反复发现的现象许多 Gerber 解析上的 bug最终都不是标准不熟而是“没有制作足够多样的测试文件”。我后来养成了一个小习惯——电脑里常年备着一套各 EDA 工具导出的测试套件AD 的、PADS 的、Allegro 的、KiCad 的每种工具出两个版本一个简单线条板一个复杂混合层板。每改一次解析逻辑就全部回归跑一遍。就是这么笨的土办法帮我挡掉了至少三次上线后才会暴露的回归 bug。如果你也想自己写一版自用 Gerber 查看器我建议从备测试文件这件事开始做起这个起点比先去翻任何源码都更踏实。本文还有配套的精品资源点击获取
返回列表