parent
43e18d7dfe
commit
97e941e548
23
README.md
23
README.md
|
@ -1,3 +1,24 @@
|
|||
# kiCad2JlcSMT
|
||||
|
||||
将 KiCAD 输出的 BOM 文件和原件放置(POS)文件, 转换为嘉立创 SMT 可用的格式.
|
||||
将 KiCAD 7.0 输出的 BOM 文件和元件放置(POS)文件, 转换为嘉立创 SMT 可用的格式.
|
||||
|
||||
## 生成 BOM 和 POS 文件
|
||||
|
||||
需要使用从原理图工具导出的 BOM 文件和从 PCB 图工具生成的元件放置(POS)文件.
|
||||
|
||||
- BOM 的生成: 原理图工具->工具->生成 BOM->bom_csv_grouped_by_value_with_fp
|
||||
- POS 的生成: PCB 工具->文件->制造输出->元件放置文件(.pos)
|
||||
|
||||
## kiCad2JlcSMT 工具的使用方法
|
||||
|
||||
Windows 下可以直接双击运行软件, 此时以命令行模式启动, 按提示输入 BOM 文件和 POS 文件的路径即可.
|
||||
|
||||
也可以通过 -t 参数指定是转换 BOM 还是转换 POS, 此时需要 -b 和 -p 来指定所使用的 BOM 或 POS 文件, 具体可传入 --help 查看.
|
||||
|
||||
## kiCad2JlcSMT 的编译
|
||||
|
||||
进入源码文件夹, 输入以下命令编译:
|
||||
|
||||
```bash
|
||||
go build
|
||||
```
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func transBom(namei string) {
|
||||
if namei == "" {
|
||||
fmt.Println("Please input a KiCAD BOM CSV file.")
|
||||
fmt.Scanln(&namei)
|
||||
}
|
||||
fmt.Println("Input from", namei)
|
||||
namei = filepath.ToSlash(namei)
|
||||
bi, e := os.ReadFile(namei)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
content := string(bi)
|
||||
content = strings.ReplaceAll(content, "\r", "")
|
||||
commentId, designatorId, footprintId, contents := findBomContents(content)
|
||||
outStr := "\"Comment\",\"Designator\",\"Footprint\"\n"
|
||||
outStr += transBomFormat(commentId, designatorId, footprintId, contents)
|
||||
oname := filepath.Join(filepath.Dir(namei), "JLCBom.csv")
|
||||
fmt.Println("Output to", oname)
|
||||
os.WriteFile(oname, []byte(outStr), 0664)
|
||||
fmt.Println("Done.")
|
||||
fmt.Println("Done. Press any key to continue.")
|
||||
fmt.Scanln(&namei)
|
||||
}
|
||||
|
||||
func transBomFormat(commentId, designatorId, footprintId int, contents []string) string {
|
||||
ostr := ""
|
||||
for _, line := range contents {
|
||||
lines := strings.Split(line, "\",")
|
||||
if commentId < len(lines) && designatorId < len(lines) && footprintId < len(lines) {
|
||||
footprint := transBomFootprint(lines[footprintId])
|
||||
ostr += lines[commentId] + "\"," + lines[designatorId] + "\"," + footprint + "\"\n"
|
||||
}
|
||||
}
|
||||
return ostr
|
||||
}
|
||||
|
||||
func transBomFootprint(footprint string) string {
|
||||
f, e := FootprintTransMap[footprint]
|
||||
if e {
|
||||
return f
|
||||
}
|
||||
return footprint
|
||||
}
|
||||
|
||||
func findBomContents(content string) (commentId, designatorId, footprintId int, contents []string) {
|
||||
contents = strings.Split(content, "\n")
|
||||
for i, line := range contents {
|
||||
if (strings.Contains(line, "\"Ref\"")) &&
|
||||
(strings.Contains(line, "\"Value\"")) &&
|
||||
(strings.Contains(line, "\"Footprint\"")) {
|
||||
lines := strings.Split(line, ",")
|
||||
for j, item := range lines {
|
||||
if item == "\"Ref\"" {
|
||||
designatorId = j
|
||||
} else if item == "\"Value\"" {
|
||||
commentId = j
|
||||
} else if item == "\"Footprint\"" {
|
||||
footprintId = j
|
||||
}
|
||||
}
|
||||
contents = contents[i+1:]
|
||||
return commentId, designatorId, footprintId, contents
|
||||
}
|
||||
}
|
||||
return 0, 0, 0, nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var FootprintTransMap map[string]string = map[string]string{
|
||||
"\"Capacitor_SMD:C_0603_1608Metric": "\"0603",
|
||||
"\"Capacitor_SMD:C_0805_2012Metric": "\"0805",
|
||||
"\"Capacitor_SMD:C_1206_3216Metric": "\"1206",
|
||||
"\"Capacitor_SMD:C_1210_3225Metric": "\"1210",
|
||||
"\"LED_SMD:LED_0603_1608Metric": "\"0603",
|
||||
"\"Diode_SMD:D_SMA": "\"SMA",
|
||||
"\"Diode_SMD:D_SOD-123": "\"SOD-123",
|
||||
"\"Package_TO_SOT_SMD:SOT-23": "\"SOT-23",
|
||||
"\"Package_TO_SOT_SMD:SOT-223-3_TabPin2": "\"SOT-223",
|
||||
"\"Package_SO:SO-4_4.4x3.6mm_P2.54mm": "\"SOP-4",
|
||||
"\"Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm": "\"SMD_3225-4P",
|
||||
"\"Package_SO:SOIC-8_3.9x4.9mm_P1.27mm": "\"SOP-8",
|
||||
"\"Resistor_SMD:R_0603_1608Metric": "\"0603",
|
||||
"\"Resistor_SMD:R_1206_3216Metric": "\"1206",
|
||||
"\"Resistor_SMD:R_1210_3225Metric": "\"1210",
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Println("kiCad2JlcSMT v1.0.2")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var nameB, nameP string
|
||||
var t uint
|
||||
|
||||
flag.Usage = usage
|
||||
flag.StringVar(&nameB, "b", "", "Input BOM CSV File")
|
||||
flag.StringVar(&nameP, "p", "", "Input POS CSV File")
|
||||
flag.UintVar(&t, "t", 2, "0-Bom, 1-Pos, other-All")
|
||||
flag.Parse()
|
||||
if t == 0 {
|
||||
transBom(nameB)
|
||||
} else if t == 1 {
|
||||
transPos(nameP)
|
||||
} else {
|
||||
transBom(nameB)
|
||||
transPos(nameP)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func transPos(namei string) {
|
||||
if namei == "" {
|
||||
fmt.Println("Please input a KiCAD POS CSV file.")
|
||||
fmt.Scanln(&namei)
|
||||
}
|
||||
fmt.Println("Input from", namei)
|
||||
namei = filepath.ToSlash(namei)
|
||||
bi, e := os.ReadFile(namei)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
content := string(bi)
|
||||
content = strings.ReplaceAll(content, "\r", "")
|
||||
designatorId, xId, yId, layerId, rotationId, contents := findPosContents(content)
|
||||
outStr := "\"Designator\",\"Mid X\",\"Mid Y\",\"Layer\",\"Rotation\"\n"
|
||||
outStr += transPosFormat(designatorId, xId, yId, layerId, rotationId, contents)
|
||||
oname := filepath.Join(filepath.Dir(namei), "JLCPos.csv")
|
||||
fmt.Println("Output to", oname)
|
||||
os.WriteFile(oname, []byte(outStr), 0664)
|
||||
fmt.Println("Done.")
|
||||
fmt.Println("Done. Press any key to continue.")
|
||||
fmt.Scanln(&namei)
|
||||
}
|
||||
|
||||
func transPosFormat(designatorId, xId, yId, layerId, rotationId int, contents []string) string {
|
||||
ostr := ""
|
||||
for _, line := range contents {
|
||||
// 注意后面字段部分没有引号:"C1","0.1uF/10V","C_0603_1608Metric",154.498000,-107.311000,-90.000000,top
|
||||
lines := strings.Split(line, "\",")
|
||||
l := lines[len(lines)-1]
|
||||
lines = lines[:len(lines)-1]
|
||||
lines = append(lines, strings.Split(l, ",")...)
|
||||
if designatorId < len(lines) &&
|
||||
xId < len(lines) &&
|
||||
yId < len(lines) &&
|
||||
layerId < len(lines) &&
|
||||
rotationId < len(lines) {
|
||||
var layer string
|
||||
switch lines[layerId] {
|
||||
case "top":
|
||||
layer = "T"
|
||||
case "bottom":
|
||||
layer = "B"
|
||||
}
|
||||
ostr += lines[designatorId] + "\"," +
|
||||
"\"" + lines[xId] + "\"," +
|
||||
"\"" + lines[yId] + "\"," +
|
||||
"\"" + layer + "\"," +
|
||||
"\"" + lines[rotationId] + "\"\n"
|
||||
}
|
||||
}
|
||||
return ostr
|
||||
}
|
||||
|
||||
func findPosContents(content string) (designatorId, xId, yId, layerId, rotationId int, contents []string) {
|
||||
contents = strings.Split(content, "\n")
|
||||
for i, line := range contents {
|
||||
if (strings.Contains(line, "Ref")) &&
|
||||
(strings.Contains(line, "PosX")) &&
|
||||
(strings.Contains(line, "PosY")) &&
|
||||
(strings.Contains(line, "Rot")) &&
|
||||
(strings.Contains(line, "Side")) {
|
||||
lines := strings.Split(line, ",")
|
||||
for j, item := range lines {
|
||||
if item == "Ref" {
|
||||
designatorId = j
|
||||
} else if item == "PosX" {
|
||||
xId = j
|
||||
} else if item == "PosY" {
|
||||
yId = j
|
||||
} else if item == "Side" {
|
||||
layerId = j
|
||||
} else if item == "Rot" {
|
||||
rotationId = j
|
||||
}
|
||||
}
|
||||
contents = contents[i+1:]
|
||||
return designatorId, xId, yId, layerId, rotationId, contents
|
||||
}
|
||||
}
|
||||
return 0, 0, 0, 0, 0, nil
|
||||
}
|
Loading…
Reference in New Issue