文号
926012
阅读
25545
回复
4
收藏
1
立创EDA转KiCAD的脚本
2023/10/07
原创
IP:上海

增加了对线型等支持 修改了一些bug 目前还在继续改进 打算之后还是增加原理图也迁移

因为老在修改完善 程序也越来越长 我已经上传ghub 大家还是从XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 下载吧

.........................................

最近使用了立创的EDA 发现它的元件符号库太全了 尤其是国内常用产品 KiCAD远远不如 那么 有没有可能转它的符号库给KiCAD用

经研究 发现立创EDA使用SQLite3数据库存储其数据 比如个人建立一个元件库 然后从各处找到感兴趣的元件符号 就可以拷贝到自己的库 也就出现在本地存放的.elib数据库中 在表Components的dataStr字段里  格式大致如下

lceda-elib.png

其实也可以在元件编辑器的菜单"文件"->"查看源码"里面得到

或者在自己工程的备份压缩文件里 各元件符号以.esym后缀的文件独立存放

批量导出数据库字段变成文件并不难 我也不多说 也暂时不需要 只是想把工程中遇到的元件加入KiCad的元件库中 因为我大概是购买了 以后也会用的 所以做了一个脚本 用来转换.esym文件为KiCad的.kicad_sym文件 处理文本 当然是perl最擅长 所以用perl

use JSON;
my %pintypes=('IN' => 'input', 'OUT' => 'output');
die "Usage: perl $0 <.esym file name> <output filename or - (STDOUT) or empty (use symbol name)>\n" if $#ARGV<0;
die "File '$ARGV[0]' is not found\n" unless open F, "<$ARGV[0]";
$symb=from_json('['.join(',', <F>).']');
close F;
#get all parts 
my @parts;
foreach (@$symb) {
  push @parts, [] if $_->[0] eq 'PART';
  push @{$parts[-1]}, $_ if $#parts>=0;
}
#["FONTSTYLE","st4",null,null,"Times new roman",15,0,0,0,null,1,0]
my %fonts=map {$_->[1] => $_->[5] && '(font (size '.($_->[5]*0.254).' '.($_->[5]*0.254).'))' || ''} grep {$_->[0] eq 'FONTSTYLE'} @$symb;
#["LINESTYLE","st19",null,3,"#330033",null]
my %linestyles=map {$_->[1] => '(stroke (width '. ($_->[5]*0.254 || 0).') (type '.($_->[3] && qw(solid dash dot dash_dot)[$_->[3]] || 'default').')'
                                                  .($_->[2]=~/#([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])/i && " (color ${\hex($1)} ${\hex($2)} ${\hex($3)} 1)" || '').')'
                                                  .($_->[4]=~/#([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])/ && " (fill (type color) (color ${\hex($1)} ${\hex($2)} ${\hex($3)} 1))" || ' (fill (type none))')}
                         grep {$_->[0] eq 'LINESTYLE'} @$symb;
#get symbol name (but there's only part name in file, so the first part name is adopted)
my $name=$parts[0]->[0]->[1]=~s/[\-#\$_\.:,]\d*$//r;
unless ($#ARGV>0 && $ARGV[0] eq '-') {
  $#ARGV>0 &&  (open STDOUT,">$ARGV[1]" or die "Failed to open '$ARGV[1]' for writing\n") or
  open STDOUT, ">$name.kicad_sym" or
  open STDOUT, '>'.($ARGV[0]=~s/\.[^\.]*$//r).'.kicad_sym'
  or die "Failed to open '".($ARGV[0]=~s/\.[^\.]*$//r).".kicad_sym' for writing\n";
}
#start to write header
print  <<"EOF";
(kicad_symbol_lib (version 20220914) (generator esym2kicad_pl)
  (symbol "$name" (in_bom yes) (on_board yes)
    (property "Reference" "${\map {split /\?$/, $_->[4]} grep {$_->[0] eq 'ATTR' && $_->[3] eq 'Designator' } @$symb}" (at -5.08 6.35 0)
      (effects (justify left))
    )
    (property "Value" "${\map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[3] eq 'Symbol' } @$symb}" (at -5.08 8.89 0)
      (effects (justify left))
    )
    (property "Footprint" "${\map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[3] eq 'Footprint' } @$symb}" (at 0 0 0)
      (effects hide)
    )
    (property "Datasheet" "${\map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[3] eq 'link' } @$symb}" (at 0 0 0)
      (effects  hide)
    )
EOF
#do every parts
my $part_suffix=1;
foreach my $part(@parts) {
  print  <<"EOF"; # there seems to be a rule about symbol's name, which is not compatible with LcEDA
    (symbol "${name}_${\$part_suffix++}_1"
EOF
  #["RECT","e3",-60,-55,60,55,0,0,0,"st1",0]
  foreach (grep {$_->[0] eq 'RECT'} @$part) {
    print <<"EOF";
      (rectangle (start ${\($_->[2]*0.254)} ${\($_->[3]*0.254)}) (end ${\($_->[4]*0.254)} ${\($_->[5]*0.254)})
        $linestyles{$_->[9]}
      )
EOF
  }
  #["ARC","e14",4.44,-6.9,1.66049,0.08325,4.53,7.03,"st1",0]
  foreach (grep {$_->[0] eq 'ARC'} @$part) {
    eval {  # will fail if 3 points is in a line
      # can't believe that kiCAD does not support arcs with angles >180 degree, so hard to fix it
      my ($x1,$y1,$x2,$y2,$x3,$y3)=($_->[2]*0.254,$_->[3]*0.254,$_->[4]*0.254,$_->[5]*0.254,$_->[6]*0.254,$_->[7]*0.254);
      my ($x21,$x32,$x13,$y21,$y32,$y13)=($x2-$x1,$x3-$x2,$x1-$x3,$y2-$y1,$y3-$y2,$y1-$y3);
      my ($a,$b)=($x21*$y32-$y21*$x32, $x21*$y13-$y21*$x13);  # 0 means 3 points is in a line
      my ($c,$d)=($x1*$x1+$y1*$y1-$x2*$x2-$y2*$y2, $x1*$x1+$y1*$y1-$x3*$x3-$y3*$y3);
      my ($x0,$y0)=(-($y13*$c+$y21*$d)/$b/2, ($x21*$d+$x13*$c)/$b/2);
      my ($s1,$s3,$pi2)=(atan2($y1-$y0,$x1-$x0), atan2($y3-$y0,$x3-$x0), 4*atan2(1,0));
      my $ang=$a>0?($s3-$s1):($s1-$s3); $ang+=$pi2 if ($ang<0); #CCW running angle
      if ($ang>3.14) { # have to split it
        my $r=sqrt(($x1-$x0)*($x1-$x0)+($y1-$y0)*($y1-$y0));
        my ($x4,$y4)=($x0+$r*cos($s1+$ang/2), $y0+$r*sin($s1+$ang/2));
        my ($x5,$y5)=($x0+$r*cos($s1+$ang/4), $y0+$r*sin($s1+$ang/4));
        my ($x6,$y6)=($x0+$r*cos($s3-$ang/4), $y0+$r*sin($s3-$ang/4));
        print <<"EOF";
      (arc (start $x1 $y1) (mid $x5 $y5) (end $x4 $y4)
        $linestyles{$_->[8]}
      )
      (arc (start $x4 $y4) (mid $x6 $y6) (end $x3 $y3)
        $linestyles{$_->[8]}
      )
EOF
      } else {
        print <<"EOF";
      (arc (start $x1 $y1) (mid $x2 $y2) (end $x3 $y3)
        $linestyles{$_->[8]}
      )
EOF
      }
    };
  } 
  #["POLY","e11",[-2,8,-2,-8],0,"st1",0]
  foreach (grep {$_->[0] eq 'POLY'} @$part) {
    print <<"EOF";
      (polyline
        (pts
EOF
    my $pts=$_->[2];
     for (my $i=0; $i<$#$pts; $i+=2) {
    print <<"EOF";
          (xy ${\($pts->[$i]*0.254)} ${\($pts->[$i+1]*0.254)})
EOF
    }
    print <<"EOF";
        )
        $linestyles{$_->[4]}
      )
EOF
  } 
  #["BEZIER","e211",[-140,-20,-120,0,-90,0,-80,-10],"st1",0], not supported, just take a pose
  foreach (grep {$_->[0] eq 'BEZIER'} @$part) {
    print <<"EOF";
      (polyline
        (pts
EOF
    my $pts=$_->[2];
     for (my $i=0; $i<$#$pts; $i+=2) {
    print <<"EOF";
          (xy ${\($pts->[$i]*0.254)} ${\($pts->[$i+1]*0.254)})
EOF
    }
    print <<"EOF";
        )
        $linestyles{$_->[3]}
      )
EOF
  } 
  #["CIRCLE","e4",-55,50,1.5,"st1",0]
  foreach (grep {$_->[0] eq 'CIRCLE'} @$part) {
    print  <<"EOF";
      (circle (center ${\($_->[2]*0.254)} ${\($_->[3]*0.254)}) (radius ${\($_->[4]*0.254)})
        $linestyles{$_->[5]}
      ) 
EOF
  } 
  #["ELLIPSE","e206",-100,20,10,20,0,"st1",0], not supported, just take a pose
   foreach (grep {$_->[0] eq 'ELLIPSE'} @$part) {
    print  <<"EOF";
      (circle (center ${\($_->[2]*0.254)} ${\($_->[3]*0.254)}) (radius ${\(($_->[4]+$_->[5])*0.127)})
        $linestyles{$_->[7]}
      ) 
EOF
  } 
 
  #["PIN","e5",1,1,-70,45,10,0,null,0,0,1]
  #["ATTR","e6","e5","NAME","VSS",false,true,-56.3,39.08502,0,"st4",0]
  #["ATTR","e7","e5","NUMBER","1",false,true,-60.5,44.08502,0,"st5",0]
  #["ATTR","e8","e5","Pin Type","IN",false,false,-70,45,0,"st2",0]
  foreach (grep {$_->[0] eq 'PIN'} @$part) {
    my $id=$_->[1];
    print  <<"EOF";
      (pin ${\($pintypes{(map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[2] eq $id && $_->[3] eq 'Pin Type'} @$part)[0]} ||  'passive')} line (at ${\($_->[4]*0.254)} ${\($_->[5]*0.254)} $_->[7]) (length ${\($_->[6]*0.254)})
        (name "${\map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[2] eq $id && $_->[3] eq 'NAME' } @$part}")
        (number "${\map {$_->[4]} grep {$_->[0] eq 'ATTR' && $_->[2] eq $id && $_->[3] eq 'NUMBER' } @$part}"\)
      )
EOF
  } 
  #["TEXT","e313",908,160,0,"yyy","st4",0]
  foreach (grep {$_->[0] eq 'TEXT'} @$part) {
    my $id=$_->[1];
    my $style=$_->[6];
    print  <<"EOF";
      (text "$_->[5]" (at ${\($_->[2]*0.254)} ${\($_->[3]*0.254)} ${\($_->[4]*10)})
        (effects $fonts{$_->[6]})
      )
EOF
  } 
print  <<"EOF";
    )
EOF
}
print  <<"EOF";
  )
)
EOF

close STDOUT;

目前支持引脚 文字 方框 圆 弧 折线 基本够用了 目前测试下来 基本都没有问题

当然 这需要一个个元件加入KiCad 正好顺便检查 批量导入其实大概也有方法的 .kicad_sym文件其实也是库文件 只需要在顶层的kicad_symbol_lib级别下放多个symbol就是了 元件文件就只放一个  至于修改脚本还是后期用copy合并后去掉重复的kicad_symbol_lib头 都不难 但我就不做了

............................... 10.8 .....

更新了脚本  增加了对多单元器件的支持 (比如双运放之类)

顺便 也把从.elib eprj导出所有元件的脚本做了

use DBI;

die "Usage: perl $0 <.elib .eprj file name> \n\tIt will extract all symbols and save them to this directory." if $#ARGV<0;
die "Unable to find '$ARGV[0]'\n" unless -e $ARGV[0];
$dbh=DBI->connect("DBI:SQLite:dbname=$ARGV[0]", '', '', {PrintError=>0}) or  die "Unable to connect file '$ARGV[0]': ".DBI::errstr."\n";
$sth=$dbh->prepare("SELECT title,dataStr from components where docType=2 and title is not null and title!=''") and $sth->execute()>=0 or  die "Failed to extract symbol data from file '$ARGV[0]': ".DBI::errstr."\n";

while(my @row = $sth->fetchrow_array()) {
	$row[0]=~tr/\*\|\"\?\/\\\&;\<\> /_/;
	$row[1]=~s/\r*(?=\n)//gm;;
	next unless $row[0] && $row[1];
	next unless open F, ">$row[0].esym";
	print F $row[1];
	close F; 
}
$sth->finish();
$dbh->disconnect();

还包括把多个.kicad_sym文件合并成一个库的脚本 (这样 导出 for %f in (*.esym) do .... 然后再合并 库迁移就OK了)

die "Usage: perl $0 <.output file name or '-' (STDOUT)>\n\t\All .kicad_sym file (which should be created by esym2kicad.pl) will be packed to a signle lib file\n" if $#ARGV<0;
die "Unable to open '$ARGV[0]'\n" unless $ARGV[0] eq '-' || open STDOUT,">$ARGV[0]";
undef  $/;

print  <<"EOF";
(kicad_symbol_lib (version 20220914) (generator makekicadlib_pl)
EOF
foreach (grep {!-d $_ && -e $_ && !/^\./ } <'*.kicad_sym'>) {
	print STDERR "processing $_\n";
	next unless open T, "<$_";
	$_=<T>;
	close T;
	print "  $1\n" if /(\(\s*symbol\s(.|\n)*?)(\s|\n)*\)[^\)]*$/i;
}
print  <<"EOF";
)
EOF
close STDOUT;


[ 修改于3年1个月前 - 2023/10/10 20:18:37 ]
+1
科创币
大仙
2023/10/29
好人一生平安
+1
科创币
Zhang_TTL
2023/11/09
有用的工具,感谢开发
m24h作者
3年1个月前IP:上海
926180
2楼

原理图的转换也做好了 代码太长 就不贴了。。。实际上都有修改 代码也都长了 只能提醒还是从GitHub获取最新版。。。如果每次修正都放在这里 这帖子至少得长两页

基本能考虑的都考虑了 包括文字大小 线型 旋转 电源符号的转换 一般不带子框图的基本都没问题了

当然还是需要手工校正一些的

引用
评论
m24h作者
3年0个月前IP:上海
926638
4楼
引用helium发表于3楼的内容
立创EDA就是经常要提示更新还要登账号这一点很反人类,还是AD没这个问题,没有收录到库里边的的符号和封装我一般是直接画的,库文件找一个路径存在本机里就行,若要备份可以考虑用NAS存一份。

毕竟人家是云开发 而且它问题也不少 关键是方便啊 开发到下单速度超快 专业版有完全离线

不过最头疼的是 共享很难做到 而且我看文件数据库 因为考虑团队协作开发 里面有加入参与者的账号信息的可能 虽然我目前个人开发 账号信息是一个公开共用的立创账号 但是也有泄漏什么的可能

而且sch转到kicad也不完美 因为各家都是根据坐标位置重合 来判断连接性的 一种用英式(反而是立创用英式) 一种用国际单位 转换之后因为精度问题 有些时候就对不上

引用
评论
1
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的