4 # User Interface that generates custom QFP and SOIC packages for pcb-1.6.3
5 # Invoked from a line like
6 # esyscmd(qfp-ui $1 $2 $3)
7 # within an m4 macro triggered by pcb-1.6.3
8 # depends on having the Right [TM] m4 macro PKG_QFP in qfp.inc
9 # Copyright 1999 Larry Doolittle <LRDoolittle@lbl.gov>
11 # SOIC support added Jan 2000 Larry Doolittle
12 # Use nX==0 for that mode.
14 # Parts library added Feb 2000 Larry Doolittle
15 # That feature is still rough, but it is useful, and you get the idea
17 # Refinement of library file usage Mar 2000 Larry Doolittle
18 # Peeks at the X resource Pcb.libraryPath, uses that for a search path
19 # for qfp.dat. Appends .:$HOME to that path, and writes any updates
20 # (via the "Save" button) to $HOME/qfp.dat only.
23 # have someone else test it enough to know what needs fixing
24 # proper support for changing pin 1 location
25 # more choices of outline (at least inboard vs. outboard)
26 # more packages in default qfp.dat, double checked and tested
28 global description boardname partnum
29 set description [ lindex $argv 0 ]
30 set boardname [ lindex $argv 1 ]
31 set partnum [ lindex $argv 2 ]
33 # scaling and centering for canvas;
34 # I use max_pix=380 for big screens, and trim it down to 266 for
35 # use on my 640x480 laptop.
36 # I've never seen any QFP exceed 36 mm, so max_mm=38 should be safe.
40 set s [ expr $max_pix/$max_mm*.0254 ]
41 set c [ expr 0.5*$max_pix ]
43 # fixme ... maybe put in a search path? Get from environment?
44 set libwritedir "$env(HOME)"
46 set libpath ".:$libwritedir"
48 set libwritefile "$libwritedir/qfp.dat"
50 # default values of the actual parameters that describe the QFP
51 global istart nX nY pitch pwidth plength lX lY
61 # Define the native units for each dimension
62 # dm is "decimicrons" :-) allows exact conversion from microns or mils
63 foreach v {pwidth plength lX lY} {
70 set factor(inch) 254000
75 proc m4define { name val } {
76 puts "define(`$name', $val)"
79 proc spit_output { } {
80 global description boardname partnum
81 global pkgname istart nX nY pitch pwidth plength lX lY
83 m4define PAD_LENGTH $plength
84 m4define PAD_WIDTH $pwidth
85 m4define ISTART $istart
90 puts "PKG_GEN_QFP($description, $boardname, $partnum)"
94 proc state_encode { } {
95 global description boardname partnum
96 global pkgname istart nX nY pitch pwidth plength lX lY
97 return "$pitch $plength $pwidth $istart $nX $nY $lX $lY $partnum $description"
100 proc state_decode { s } {
101 global description boardname partnum
102 global pkgname istart nX nY pitch pwidth plength lX lY
103 regexp {([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([^ ]+) (.*)} $s dummy\
104 pitch plength pwidth istart nX nY lX lY partnum description
107 proc woohoo { x y } {
109 catch { .p.txt tag delete mine }
110 set loc [ .p.txt index "@$x,$y" ]
111 regexp {([0-9]*)\.} $loc dummy l
112 # puts "woo-hoo $x $y $loc $l"
113 regexp {([^ ]+)} [ .p.txt get $l.0 "$l.0 lineend" ] dummy k
114 if { [ catch { state_decode $library($k) } ] } return
115 .p.txt tag add mine $l.0 "$l.0 lineend"
116 .p.txt tag configure mine -background red
121 proc libfiles_read { } {
123 if { [ catch { set fd [ open "| appres Pcb" ] } ] } return
124 while { [ gets $fd line ] != -1 } {
125 regexp {([a-zA-Z.]+):[ ] *([^ ]*)} $line dummy res_name res_value
126 if { $res_name == "Pcb.libraryPath" } {
127 set libpath "$res_value:$libpath"
131 foreach f [ split $libpath ":" ] { libfile_read "$f/qfp.dat" }
134 proc libfile_read { filename } {
136 if { [ catch { set fd [ open $filename ] } ] } return
137 while { [ gets $fd line ] != -1 } {
138 regexp {[0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ ([^ ]+) .*} $line dummy pn
139 set library($pn) $line
144 proc load_library { } {
145 global library libline
146 if { [ catch { toplevel .p } ] } return
147 wm title .p "qfp-ui-library"
149 button .p.b.dismiss -text "Dismiss" -command "destroy .p"
150 pack .p.b.dismiss -side left
151 pack .p.b -side bottom
152 text .p.txt -width 40 -height 15 -font fixed \
153 -yscrollcommand ".p.sbar set"
154 scrollbar .p.sbar -command ".p.txt yview"
155 pack .p.txt -side left -fill both -expand 1
156 pack .p.sbar -side right -fill y
157 catch { unset libline }
159 set keys [ lsort [ array names library ] ]
161 regexp {[0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ ([^ ]+) (.*)} $library($d) dummy pn desc
162 paint_lib_entry $pn $desc
164 .p.txt configure -state disabled
165 bind .p.txt <Button> "woohoo %x %y"
168 proc save_library { } {
169 global library partnum description libwritefile
171 .p.txt configure -state normal
172 paint_lib_entry $partnum $description
173 .p.txt configure -state disabled
175 set library($partnum) [ state_encode ]
177 set fd [ open $libwritefile "a+" ]
178 puts $fd [ state_encode ]
183 proc paint_lib_entry { p desc } {
185 if { [ catch { set l $libline($p) } ] } {
186 set loc [ .p.txt index "end -1 lines" ]
187 regexp {([0-9]*)\.} $loc dummy libline($p)
188 .p.txt insert end "$p $desc\n"
190 .p.txt delete $l.0 "$l.0 lineend"
191 .p.txt insert $l.0 "$p $desc"
195 proc uconvert { v in out } {
196 # puts "$v $in converted to $out"
198 set answer [ expr ($v*$factor($in))/$factor($out) ]
203 proc qupdate { v unit } {
204 global $v ${v}_inch ${v}_mm ${v}_native
205 set screen "${v}_${unit}"
206 set newuser [ expr \$$screen ]
207 # compute the exact result in mils
208 set native [ expr \$${v}_native ]
209 # puts "$v $unit $newuser $native"
210 if { ! [catch { set new [ uconvert $newuser $unit $native ] } ] } {
216 proc line_update { v new } {
217 global $v ${v}_inch ${v}_mm ${v}_native
218 set native [ expr \$${v}_native ]
219 # puts "$v $new $native"
220 set new [ expr round($new) ]
222 set inch [ uconvert $new.0 $native inch ]
223 set mm [ uconvert $new.0 $native mm ]
224 set ${v}_inch [ format "%.3f" $inch]
225 set ${v}_mm [ format "%.2f" $mm]
231 if { ! [ catch { set new [ expr round(\$$v) ] } ] } {
238 proc push_state_to_screen { } {
239 global pitch pwidth plength lX lY
240 foreach v {pitch pwidth plength lX lY} {
241 line_update $v [ expr \$$v ]
245 # Trickery with the part number, make it follow the live pin count,
246 # until and unless the user makes the name not include QFP-xxx.
247 # The magic value "menu" matches the third column of our entry in generic.list
248 proc part_update { } {
250 set pincount [ expr 2*($nX+$nY) ]
251 set newstring "QFP-$pincount"
252 if { $partnum == "menu" } {
253 set partnum $newstring
255 regsub -all {QFP-[0-9]+} $partnum $newstring partnum
259 proc adjustment { w number title varname } {
265 entry $f.number -textvariable $number -width 4
266 bind $f.number <FocusOut> "nupdate $varname"
267 bind $f.number <Return> "nupdate $varname"
269 label $f.label -text $title
270 global ${varname}_mm ${varname}_inch
271 entry $f.mm -textvariable "${varname}_mm" -width 8
272 entry $f.inch -textvariable "${varname}_inch" -width 8
273 pack $f.inch $f.mm $f.label $f.number -side right
274 pack $f -side top -anchor e
275 bind $f.inch <FocusOut> "qupdate $varname inch"
276 bind $f.inch <Return> "qupdate $varname inch"
277 bind $f.mm <FocusOut> "qupdate $varname mm"
278 bind $f.mm <Return> "qupdate $varname mm"
281 proc draw_pad { x y wx wy } {
283 set x1 [ expr round($c+$s*($x-0.5*$wx)) ]
284 set y1 [ expr round($c+$s*($y-0.5*$wy)) ]
285 set x2 [ expr round($x1+$s*$wx) ]
286 set y2 [ expr round($y1+$s*$wy) ]
287 # puts "rectangle $x1 $x2 $y1 $y2"
288 .c create rectangle $x1 $y1 $x2 $y2 \
289 -fill black -outline ""
292 proc draw_line { x1 y1 x2 y2 } {
294 .c create line [ expr $c+$s*$x1 ] [ expr $c+$s*$y1 ] \
295 [ expr $c+$s*$x2 ] [ expr $c+$s*$y2 ] \
296 -fill white -width 2.0
299 proc draw_dot { x y } {
302 .c create oval [ expr $c+$s*$x-$r ] [ expr $c+$s*$y-$r ] \
303 [ expr $c+$s*$x+$r ] [ expr $c+$s*$y+$r ] \
304 -fill white -outline ""
307 proc draw_pad_line { n x y dx dy wx wy } {
308 # puts "$n $x $y $dx $dy $wx $wy"
309 for { set i 0} {$i<$n} {incr i} {
310 draw_pad [ expr $x+$i*$dx ] [ expr $y+$i*$dy ] $wx $wy
314 proc draw_outline { } {
316 # use floating point mils for these calculations
317 global pitch nX nY lX lY plength pwidth
318 set p [expr $pitch.0/254 ]
319 set xmin [expr -0.5*($lX-$plength) ]
320 set xmax [expr 0.5*($lX-$plength) ]
321 set ymin [expr -0.5*($lY-$plength) ]
322 set ymax [expr 0.5*($lY-$plength) ]
323 set xstart [ expr -0.5*$p*($nX-1) ]
324 set ystart [ expr -0.5*$p*($nY-1) ]
325 draw_pad_line $nX $xstart $ymin $p 0 $pwidth $plength
326 draw_pad_line $nY $xmin $ystart 0 $p $plength $pwidth
327 draw_pad_line $nX $xstart $ymax $p 0 $pwidth $plength
328 draw_pad_line $nY $xmax $ystart 0 $p $plength $pwidth
331 draw_dot [ expr $xmin+1.5*$plength ] $ystart
333 # package outline: handle SOIC cases, too
334 set adj [ expr (($nY>0)-.5)*$plength+15 ]
335 set xmin [expr $xmin+$adj ]
336 set xmax [expr $xmax-$adj ]
338 set adj [ expr (($nX>0)-.5)*$plength+15 ]
339 set ymin [expr $ymin+$adj ]
340 set ymax [expr $ymax-$adj ]
342 draw_line $xmin $ymin $xmin $ymax
343 draw_line $xmax $ymin $xmax $ymax
344 draw_line $xmin $ymin $xmax $ymin
345 draw_line $xmin $ymax $xmax $ymax
350 proc infoline { w text var } {
354 entry $win.var -textvariable $var
355 label $win.id -text $text
356 pack $win.var $win.id -side right
357 pack $win -side top -anchor e
361 # label .debug1 -text "$argv"
362 # label .debug2 -text "$env(PATH)"
363 # pack .debug1 .debug2
367 label .a.header.inch -text "inch" -width 8
368 label .a.header.mm -text "mm" -width 8
369 pack .a.header.inch .a.header.mm -side right
370 pack .a.header -side top -anchor e
371 adjustment .a "" "Pitch" pitch
372 adjustment .a "" "Pad Width" pwidth
373 adjustment .a "" "Pad Length" plength
374 adjustment .a nX "X length" lX
375 adjustment .a nY "Y length" lY
378 infoline "" "Description: " description
379 infoline "" "Name on board: " boardname
380 infoline "" "Part Number: " partnum
383 button .b.done -text "Done" -command spit_output
384 button .b.load -text "Library" -command load_library
385 button .b.save -text "Save" -command save_library
386 # pcb-1.6.3 gronks with no input from library, so we can't
387 # give the user this option.
388 # button .b.cancel -text "Cancel" -command exit
389 pack .b.done .b.load .b.save -side right
392 canvas .c -width $max_pix -height $max_pix
394 label .whoami1 -text "Experimental QFP UI for pcb-1.6.3"
395 label .whoami2 -text "by Larry Doolittle <LRDoolittle@lbl.gov>"
396 pack .whoami1 .whoami2