您尚未登录。

楼主 # 2021-12-10 20:46:44

xfdr0805
会员
注册时间: 2020-07-23
已发帖子: 236
积分: 309

分享一个-手工贴片助手-C#开发

开发背景

一直一来,在开发完新样品做样机的时候,手工贴片非常痛苦,板子简单还好,如果板子复杂且样机数量又多(一般10台以内),那真是贴的眼疼胳膊酸啊,在贴片过程中最耗费时间的就是在密密麻麻的丝印图上找元件座位号了,所以如果有什么工具能一下子找到并能图形化标注出来位置,那将大大加快贴片的速度,也不用闻那么长时间的锡膏散发出来的有毒气体了。

灵感来源

之前在用Kicad设计板子的时候,有一款插件不得不提一下,就是InteractiveHtmlBom,这款插件可以生成一个交互式BOM的文件,可以在浏览器里打开,图形化展示出来各个元件位置,手工贴片非常方便
image1.png
这正是我想要的效果,但是这个插件只适合用在Kicad或者LCEDA里,其它软件使用不了,而且还得是源代码才能生成交互式BOM,只能自己开发了,于是就有了这个软件

实现原理

由于没有源文件,要实现这种效果,需要有BOM表,Gerber,及坐标文件,这些是工厂批量生产的最基本的东西,都会提供的。
原理:根据BOM表获取同一种元件的座位号,再从坐标里件里获取每一个元件的坐标,然后生成每一个元件的gerber绘图语句,最后就是渲染gerber丝印图并在上边标识出来

Gerber文件解析

Gerber文件要是自己写,还是比较复杂的,网上已有很多开源的Gerber文件解析的库,就不重复造轮子了,这里使用了miltonneal54大神开源的GerberVS库,我测试了一下,已经相当完善了,测试了一些产品的gerber 基本没问题。
Gerber解析库
https://github.com/miltonneal54/GerberVS
Gerber规范
gerber_rs274xrevd_e.pdf

BOM表解析

BOM是excel格式的,这里使用开源的NPOI进行BOM表的读取,还是比较简单的

workBook = new XSSFWorkbook(tbx_bom.Text);
ISheet sheet = workBook.GetSheetAt(0);//"PCB 部分"
//计算总行数
int lastNum = sheet.LastRowNum;
Console.WriteLine("总行数:" + lastNum);
//从第1行开始
//Console.WriteLine(sheet.GetRow(3).GetCell(1).StringCellValue);
dataGridView1.Rows.Clear();
for (int i = 2; i < lastNum; i++)
{
    if (sheet.GetRow(i) != null)
    {
        //Console.WriteLine(i + " ---> " + sheet.GetRow(i).Count());
        dataGridView1.Rows.Add();
        for (int j = 0; j < 10; j++)
        {
            if (sheet.GetRow(i).GetCell(j) != null)
            {
                if (sheet.GetRow(i).Cells.Count > 10)
                {
                    //dataGridView1.Rows[i].Cells[j].Value = sheet.GetRow(i).GetCell(j).StringCellValue;
                    switch (sheet.GetRow(i).GetCell(j).CellType)
                    {
                        case CellType.Numeric:
                            //Console.WriteLine(j + " ---> " + sheet.GetRow(i).GetCell(j).NumericCellValue);
                            dataGridView1.Rows[i - 2].Cells[j].Value = sheet.GetRow(i).GetCell(j).NumericCellValue;
                            break;
                        case CellType.String:
                            //Console.WriteLine(j + " ---> " + sheet.GetRow(i).GetCell(j).StringCellValue);
                            dataGridView1.Rows[i - 2].Cells[j].Value = sheet.GetRow(i).GetCell(j).StringCellValue;

                            break;

                    }

                }
                else
                {
                    Console.WriteLine(i + " ---> cells < 10");
                }

            }

        }
    }


}
//dataGridView1.AutoResizeColumns();
dataGridView1.AutoResizeRows();
坐标文件解析

坐标文件就是普通的文本文件,按照坐标格式进行读取就可以了

StreamReader sr = File.OpenText(tbx_rep.Text);
pst = new List<Placement>();
while ((temp = sr.ReadLine()) != null)
{

    if (temp.Contains("Total Number of SMD Components on Top"))
    {
        string s = temp.Remove(0, temp.IndexOf(':') + 1);
        line = int.Parse(s);
        sr.ReadLine();//空一行
        sr.ReadLine();//空一行
        for (int i = 0; i < line; i++)
        {

            temp = sr.ReadLine();
            //坐标文件格式 座位号 X Y 长度都为10 角度为7

            string str = temp.Substring(0, 10).Trim();
            string x = temp.Substring(10, 10).Trim();
            string y = temp.Substring(20, 10).Trim();
            string r = temp.Substring(30, 7).Trim();
            if (i == 0)
            {
                label_first_smd.Text = string.Format("{0}--->X:{1} Y:{2}", str, x, y);
            }
            Placement p = new Placement();
            p.Symbol = str;
            p.X_pos = x;
            p.Y_pos = y;
            p.Rotation = r;
            pst.Add(p);
        }
    }
}
在图形中渲染位置

根据坐标生成对应的gerber绘图语句,比如画一个圆点,能标识出即可

//获取座位号
Console.WriteLine(dataGridView1.Rows[e.RowIndex].Cells[7].Value);
string location = (string)dataGridView1.Rows[e.RowIndex].Cells[7].Value;
string[] loc;
StringBuilder sb = new StringBuilder();
sb.Append("G04 Sonavox auto mark smd commpent helper *\r\n");
sb.Append("G04 Draw by:101367 *\n");
sb.Append("%MOIN*%\n");//单位选择
sb.Append("%FSLAX55Y55*%\n");//坐标系选择
//sb.Append("%ADD12R,0.050X0.050X*%\n");//画圆点
sb.Append(string.Format("%ADD11C,{0}*%\n", numericUpDown_size.Value.ToString()));//画圆点
sb.Append("\nG54D11*\n");//选择自定义光圈
if (location != "")
{
    loc = location.Split(',');
    bool find = false;
    foreach (string s in loc)
    {
        find = false;
        foreach (Placement p in pst)
        {

            if (s == p.Symbol)
            {
                find = true;
                Int32 xpos = Int32.Parse(p.X_pos) + (Int32)numericUpDown_x.Value;
                Int32 ypos = Int32.Parse(p.Y_pos) + (Int32)numericUpDown_y.Value;
                xpos = xpos * 100;
                ypos = ypos * 100;
                sb.Append(string.Format("X{0}Y{1}D03*\n", xpos, ypos));//标识位置
                Console.WriteLine(string.Format("Top SMD:{0}--->X:{1} Y:{2} R:{3}", s, p.X_pos, p.Y_pos, p.Rotation));
                //break;
                continue;//跳过剩下的代码,继续执行循环
            }

        }
        if (find == false)
        {
            foreach (Placement p in psb)
            {
                if (s == p.Symbol)
                {
                    find = true;
                    Int32 xpos = Int32.Parse(p.X_pos) + (Int32)numericUpDown_x.Value;
                    Int32 ypos = Int32.Parse(p.Y_pos) + (Int32)numericUpDown_y.Value;
                    xpos = xpos * 100;
                    ypos = ypos * 100;
                    sb.Append(string.Format("X{0,0.00}Y{1,0.00}D03*\n", xpos, ypos));//标识位置
                    
                    Console.WriteLine(string.Format("Bottom SMD:{0}--->X:{1} Y:{2} R:{3}", s, p.X_pos, p.Y_pos, p.Rotation));
                    continue;//跳过剩下的代码,继续执行循环
                }

            }
        }

        if (find == false)
        {
            MessageBox.Show(s + "的坐标未找到,请确认!!!\r\n可能是座位号不正确或者在MI部分");
            return;
        }
    }
    sb.Append("M02*\n");
    richTextBox1.Text = sb.ToString();
    //保存到文件,通知gerberV加载,reload即可刷新视图
    StreamWriter sw = new StreamWriter(@"Auto-Generate-Gerber.gbx");
    sw.Write(sb.ToString());
    sw.Flush();
    sw.Close();
    reload_event(0);
}
软件界面

图形标记目前只支持加点,可以很容易添加方框,箭头等符号
image4.png
软件以坐标文件中第一个位置为参照来计算偏移量的,需要填入实际坐标才可以计算出偏移量,如果坐标没有偏移,则无需计算,贴完的可以标记为绿色背景,取消即为默认背景
image2.png

源码如下

目前代码只适合解析我们cadstar导出的文件,这个EDA国内用的不多,这里给出源码,简单修改下就可以解析其它格式的BOM及坐标文件了,有需要的朋友可以参考
点击下载源码
GerberVS.7z

视频演示

离线

楼主 #11 2021-12-15 10:01:06

xfdr0805
会员
注册时间: 2020-07-23
已发帖子: 236
积分: 309

Re: 分享一个-手工贴片助手-C#开发

海石生风 说:

能做成目前这样,看得出已经花了不少精力了。但我认为更快的实现方式可以是借用InteractiveHtmlBom,修改它的文件解析这一步骤其它少做修改可能会少点工作量。
现在前端有WASM,要比以前只有javascript的时代灵活不少。

其实没花多少精力 一上午就完成了 因为有大神已经完成了gerber解析并渲染了 我只是读取座位号及数量然后从坐标文件里获得每个元件的坐标并生成gerber绘图语句就完了 只有这么一丢丢的工作量  比起gerber解析渲染 可以忽略
关于那个插件 我觉得插件是读取的源文件的方式生成的 因为无需导出gerber bom 坐标文件就可以生成  并且lceda也可以用这个插件生成  那个也是源文件  在没pcb源文件的情况下用不了  这是个人猜测 没深究 所以就用C#写了一个  如果有kicad或者lceda源文件 用那个插件更好

离线

楼主 #14 2022-03-28 15:33:59

xfdr0805
会员
注册时间: 2020-07-23
已发帖子: 236
积分: 309

Re: 分享一个-手工贴片助手-C#开发

海石生风 说:

不用纠结AD了,画大板用Kicad、画小板用LCEDA

确实 我现在全部kicad画 用起来很顺手  lceda早已不用了

离线

楼主 #15 2022-03-28 15:43:05

xfdr0805
会员
注册时间: 2020-07-23
已发帖子: 236
积分: 309

Re: 分享一个-手工贴片助手-C#开发

john78 说:

曾经也想做个,分析完 99se到处坐标,后来。。。直接买了个国产贴片机

哈哈 曾经想买个贴片机  后来 ... 领导不同意 于是分析了gerber文件及坐标 写了这个小工具

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn