diff --git a/kokopelli/pcb.py b/kokopelli/pcb.py new file mode 100644 index 0000000000000000000000000000000000000000..7d4b139020a0391e45f453c64ad91a4624b4140b --- /dev/null +++ b/kokopelli/pcb.py @@ -0,0 +1,988 @@ +import operator +from math import cos, sin, atan2, radians, degrees, sqrt + +import koko.lib.shapes2d as s2d +from koko.lib.text import text +from numpy import * + +class PCB(object): + def __init__(self, x0, y0, width, height, chamfer_distance=0): + self.x0 = x0 + self.y0 = y0 + self.width = width + self.height = height + + self.components = [] + self.connections = [] + self._cutout = None + self.custom_cutout = None + self.custom_layers = {} + self.chamfer_distance = chamfer_distance + + @property + def traces(self): + L = [c.pads for c in self.components if c.side == 0] + [c.traces[0] for c in self.connections] + if L: + t = reduce(operator.add, L) + #L = [c.holes for c in self.components if c.holes is not None] + #L.extend([c.holes for c in self.connections if c.holes is not None]) + #if L: + # t = t - reduce(operator.add,L) + return t + else: return None + @property + def traces_other_side(self): + L = [c.pads for c in self.components if c.side == 1] + [c.traces[1] for c in self.connections if c.traces[1] is not None] + if L: + t = reduce(operator.add, L) + #L = [c.holes for c in self.components if c.holes is not None] + #L.extend([c.holes for c in self.connections if c.holes is not None]) + #if L: + # t = t - reduce(operator.add,L) + return t + else: return None + @property + def holes(self): + L = [c.holes for c in self.components if c.holes is not None] + L.extend([c.holes for c in self.connections if c.holes is not None]) + if L: + t = reduce(operator.add,L) + return t + else: + return None + + @property + def part_labels_top(self): + L = [c.label for c in self.components if c.label is not None and c.side==0] + return reduce(operator.add, L) if L else None + @property + def part_labels_bot(self): + L = [c.label for c in self.components if c.label is not None and c.side==1] + return reduce(operator.add, L) if L else None + @property + def part_shadows_top(self): + L = [c.shadow_shape for c in self.components if c.shadow_shape is not None and c.side==0] + return reduce(operator.add, L) if L else None + @property + def part_shadows_bot(self): + L = [c.shadow_shape for c in self.components if c.shadow_shape is not None and c.side==1] + return reduce(operator.add, L) if L else None + @property + def pin_labels_top(self): + L = [c.pin_labels for c in self.components if c.pin_labels is not None and c.side==0] + return reduce(operator.add, L) if L else None + @property + def pin_labels_bot(self): + L = [c.pin_labels for c in self.components if c.pin_labels is not None and c.side==1] + return reduce(operator.add, L) if L else None + + @property + def cutout(self): + if self.custom_cutout is not None: + if self.holes: + return self.custom_cutout - self.holes + else: + return self.custom_cutout + outer = s2d.rectangle(self.x0, self.x0 + self.width, + self.y0, self.y0 + self.height) + if self.chamfer_distance: + c = self.chamfer_distance + c1 = s2d.triangle(self.x0,self.y0,self.x0,self.y0+c,self.x0+c,self.y0) + c2 = s2d.triangle(self.x0+self.width,self.y0+self.height, self.x0+self.width, self.y0+self.height-c, self.x0+self.width-c, self.y0+self.height) + c3 = s2d.triangle(self.x0,self.y0+self.height, self.x0+c, self.y0+self.height, self.x0, self.y0+self.height-c) + c4 = s2d.triangle(self.x0+self.width,self.y0, self.x0+self.width-c, self.y0, self.x0+self.width, self.y0+c) + outer -= c1+c2+c3+c4 + #L = [c.holes for c in self.components if c.holes is not None] + #L.extend([c.holes for c in self.connections if c.holes is not None]) + return outer - self.holes if self.holes else outer + + #@property + def layout(self,sides=[0,1]): + T = [] + if 0 in sides: + if self.part_labels_top: + T.append(s2d.color(self.part_labels_top, (125, 200, 60))) + if self.pin_labels_top: + T.append(s2d.color(self.pin_labels_top, (255, 90, 60))) + if self.traces: + T.append(s2d.color(self.traces-self.holes, (125, 90, 60))) + if self.part_shadows_top: + T.append(s2d.color(self.part_shadows_top-self.holes,(55,55,60))) + if 1 in sides: + if self.part_labels_bot: + T.append(s2d.color(self.part_labels_bot, (90, 60, 255))) + if self.pin_labels_bot: + T.append(s2d.color(self.pin_labels_bot, (175, 30, 175))) + if self.traces_other_side: + T.append(s2d.color(self.traces_other_side-self.holes, (90, 60, 125))) + if self.part_shadows_bot: + T.append(s2d.color(self.part_shadows_bot-self.holes,(45, 30, 62))) + + for v in sorted(self.custom_layers.values(),key=lambda v: -v['position']): + if v['visible']: T.append(s2d.color(v['layer'],v['color'])) + T.append(s2d.color(self.cutout, (35,35,40))) + return T + + + def __iadd__(self, rhs): + if isinstance(rhs, Component): + self.components.append(rhs) + elif isinstance(rhs, Connection): + self.connections.append(rhs) + else: + raise TypeError("Invalid type for PCB addition (%s)" % type(rhs)) + return self + + def add_custom_layer(self,name,layer,color): + self.custom_layers[name] = {'layer':layer,'color':color,'position':len(self.custom_layers),'visible':1} + def hide_layer(self,name): + self.custom_layers[name]['visible'] = 0 + + def connectH(self, *args, **kwargs): + ''' Connects a set of pins or points, traveling first + horizontally then vertically + ''' + width = kwargs['width'] if 'width' in kwargs else 0.016 + mode = kwargs['mode'] if 'mode' in kwargs else 'explicit' + sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]] + new_sides = [] + points = [] + args = list(args) + for i,p in enumerate(args): + if not isinstance(p,BoundPin): + if mode=='diff': + args[i] = Point(args[i-1].x+p[0],args[i-1].y+p[1]) + elif mode=='explicit': + args[i] = Point(*p) + else: + raise NotImplementedError("Unknown mode type %s"%mode) + for A, B, s in zip(args[:-1], args[1:], sides): + points.append(A); new_sides.append(s) + if (A.x != B.x): + points.append(Point(B.x, A.y)); new_sides.append(s) + if A.y != B.y: points.append(B) + c = Connection(width, *points, sides=new_sides) + self.connections.append(c) + return c + + def connectV(self, *args, **kwargs): + ''' Connects a set of pins or points, travelling first + vertically then horizontally. + ''' + width = kwargs['width'] if 'width' in kwargs else 0.016 + mode = kwargs['mode'] if 'mode' in kwargs else 'explicit' + sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]] + new_sides = [] + points = [] + args = list(args) + for i,p in enumerate(args): + if not isinstance(p,BoundPin): + if mode=='diff': + args[i] = Point(args[i-1].x+p[0],args[i-1].y+p[1]) + elif mode=='explicit': + args[i] = Point(*p) + else: + raise NotImplementedError("Unknown mode type %s"%mode) + for A, B, s in zip(args[:-1], args[1:], sides): + points.append(A); new_sides.append(s) + if (A.y != B.y): + points.append(Point(A.x, B.y)); new_sides.append(s) + if A.x != B.x: points.append(B) + c = Connection(width, *points, sides=new_sides) + self.connections.append(c) + return c + + def connectD(self, *args, **kwargs): + ''' Connects a set of pins or points, travelling first + diagonally then horizontally or vertically, depending on geometry. + ''' + width = kwargs['width'] if 'width' in kwargs else 0.016 + sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]] + new_sides = [] + points = [] + def sgn(x): + if x>=0: + return 1 + else: + return -1 + args = list(args) + for i,p in enumerate(args): + if not isinstance(p,BoundPin): + args[i] = Point(*p) + for A, B, s in zip(args[:-1], args[1:], sides): + points.append(A); new_sides.append(s) + if (B.y-A.y != B.x-A.x): + if abs(B.y-A.y) > abs(B.x-A.x): + points.append(Point(B.x, A.y+sgn(B.y-A.y)*abs(B.x-A.x))); new_sides.append(s) + else: + points.append(Point(A.x+sgn(B.x-A.x)*abs(B.y-A.y),B.y)); new_sides.append(s) + if (A.x != B.x) or (A.y != B.y): points.append(B) + c = Connection(width, *points, sides=new_sides) + self.connections.append(c) + return c + + +################################################################################ + +class Component(object): + ''' Generic PCB component. + ''' + def __init__(self, x, y, rot=0, name='',label_size=0.05, side=0): + ''' Constructs a Component object + x X position + y Y position + rotation angle (degrees) + name String + side which side of board 0 for top, 1 for bottom + ''' + self.x = x + self.y = y + self.rot = rot + self.name = name + self.label_size = label_size + self.side = side + if self.side == 1: + self.pins = [p.mirror_x() for p in self.pins] + self.vias = [v.mirror_x() for v in self.vias] + + def __getitem__(self, i): + if isinstance(i, str): + try: + pin = [p for p in self.pins if p.name == i][0] + except IndexError: + raise IndexError("No pin with name %s" % i) + elif isinstance(i, int): + try: + pin = self.pins[i-1] + except IndexError: + raise IndexError("Pin %i is not in array" %i) + return BoundPin(pin, self) + + @property + def pads(self): + pads = reduce(operator.add, [p.pad for p in self.pins]) + return s2d.move(s2d.rotate(pads, self.rot), self.x, self.y) + + @property + def holes(self): + if self.vias: + holes = reduce(operator.add,[v.hole for v in self.vias]) + return s2d.move(s2d.rotate(holes,self.rot), self.x, self.y) + else: return None + + @property + def pin_labels(self): + L = [] + for p in self.pins: + p = BoundPin(p, self) + if p.pin.name: + t = s2d.rotate(text(p.pin.name, 0, 0, p.pin.label_size),self.rot+p.pin.label_rot) + L.append(s2d.move(t, p.x, p.y)) + return reduce(operator.add, L) if L else None + + @property + def label(self): + return text(self.name, self.x, self.y, self.label_size) + @property + def shadow_shape(self): + try: + return s2d.move(s2d.rotate(self.shadow, self.rot),self.x, self.y) + except AttributeError: + return None + +################################################################################ + +class Pin(object): + ''' PCB pin, with name, shape, and position + ''' + def __init__(self, x, y, shape, name='', label_size=.03, label_rot=0): + self.x = x + self.y = y + self.shape = shape + self.name = name + self.label_size = label_size + self.label_rot = label_rot + + @property + def pad(self): + return s2d.move(self.shape, self.x, self.y) + + def mirror_x(self): + return Pin( -self.x, self.y, self.shape, self.name, label_size=self.label_size, label_rot=self.label_rot ) + +################################################################################ + +class Via(object): + ''' PCB via, with shape, and position + ''' + def __init__(self, x, y, shape): + self.x = x + self.y = y + self.shape = shape + + @property + def hole(self): + return s2d.move(self.shape, self.x, self.y) + + def mirror_x(self): + return Via( -self.x, self.y, self.shape ) + +################################################################################ + +class BoundPin(object): + ''' PCB pin localized to a specific component + (so that it has correct x and y positions) + ''' + def __init__(self, pin, component): + self.pin = pin + self.component = component + + @property + def x(self): + return (cos(radians(self.component.rot)) * self.pin.x - + sin(radians(self.component.rot)) * self.pin.y + + self.component.x) + + @property + def y(self): + return (sin(radians(self.component.rot)) * self.pin.x + + cos(radians(self.component.rot)) * self.pin.y + + self.component.y) + @property + def point(self): + return Point(self.x,self.y) +################################################################################ + +class Point(object): + ''' Object with x and y member variables + ''' + def __init__(self, x, y): + self.x = x + self.y = y + def __iter__(self): + return iter([self.x, self.y]) + def __add__(self, p): + return Point(self.x+p.x,self.y+p.y) + def __sub__(self, p): + return Point(self.x-p.x,self.y-p.y) + def __rmul__(self,a): + return Point(a*self.x,a*self.y) + def magnitude(self): + return sqrt(self.x*self.x + self.y+self.y) + def normalized(self): + return Point(self.x/self.magnitude(), self.y/self.magnitude()) + @property + def point(self): + return self + +################################################################################ + +class Connection(object): + ''' Connects two pins via a series of intermediate points + ''' + def __init__(self, width, *args, **kwargs): + self.width = width + self.points = [ + a if isinstance(a, BoundPin) else Point(*a) for a in args + ] + self.sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]] #0 is base side, 1 is other side + self.holes = None + self.jumpers = [] + + def add_jumper(self,p,rot=0,width=.12, height=.07,thick=.05): + self.jumpers.append((p,rot,width,height,thick)) + return self + + def cut_corners(self,idx): + for i in idx: + i,v = i #unpack index and distance + assert(i>0) #start corner numbering at 1 + assert(i<len(self.points)) #no corner to cut at end + d = lambda p,q: sqrt( (p.x-q.x)**2 + (p.y-q.y)**2 ) + dm = d(self.points[i],self.points[i-1]) + dp = d(self.points[i],self.points[i+1]) + #if dm > dp: + self.points = self.points[:i] + \ + [Point(self.points[i].x-v/dm*(self.points[i].x-self.points[i-1].x ), self.points[i].y-v/dm*(self.points[i].y-self.points[i-1].y )), + Point(self.points[i].x+v/dp*(self.points[i+1].x-self.points[i].x ), self.points[i].y+v/dp*(self.points[i+1].y-self.points[i].y )) + ] + \ + self.points[i+1:] + self.sides.insert(i,self.sides[i]) + #else: + # self.points[i] = self.points[i] - dm/dp*(self.points[i]-self.points[i+1]) + return self + + @property + def traces(self): + #_pad_1206 = s2d.rectangle(-0.025, 0.025, -0.034, 0.034) + _pad_via = s2d.circle(0,0,.025) #s2d.rectangle(-0.025, 0.025, -0.025, 0.025) + _hole_via = s2d.circle(0,0,.016) + jumper_cuts = [] + jumper_pads = [] + for p,r,w,h,t in self.jumpers: + _jumper_pad = s2d.move(s2d.rectangle(-.5*t, .5*t, -.5*h, .5*h),-.5*w,0) + _jumper_pad += s2d.move(s2d.rectangle(-.5*t, .5*t, -.5*h, .5*h), .5*w,0) + _cut = s2d.rectangle(-.5*w,.5*w,-.5*h,.5*h) + jumper_cuts.append(s2d.move(s2d.rotate(_cut,r),p[0],p[1])) + jumper_pads.append(s2d.move(s2d.rotate(_jumper_pad,r),p[0],p[1])) + t = [[],[]] + for p1, p2, side in zip(self.points[:-1], self.points[1:], self.sides): + d = sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) + if p2 != self.points[-1]: + d += self.width/2 + a = atan2(p2.y - p1.y, p2.x - p1.x) + r = s2d.rounded_rectangle(0, d, -self.width/2, self.width/2,1.) + t[side].append(s2d.move(s2d.rotate(r, degrees(a)), p1.x, p1.y)) + try: + result0 = reduce(operator.add, t[0]) + except TypeError: + result0 = None + try: + result1 = reduce(operator.add, t[1]) + except TypeError: + result1 = None + #calculate locations for via holes and pads + for s1,s2,p in zip(self.sides[:-1],self.sides[1:],self.points[1:-1]): + if s1!=s2: + result0 += s2d.move(_pad_via,p.x,p.y) + result1 += s2d.move(_pad_via,p.x,p.y) + self.holes += s2d.move(_hole_via,p.x,p.y) + if len(self.jumpers)!=0: + result0 -= reduce(operator.add,jumper_cuts) + result0 += reduce(operator.add,jumper_pads) + return result0, result1 + +################################################################################ +# Discrete passive components +################################################################################ + +_pad_1206 = s2d.rectangle(-0.032, 0.032, -0.034, 0.034) + +class R_1206(Component): + ''' 1206 Resistor + ''' + pins = [Pin(-0.06, 0, _pad_1206), Pin(0.06, 0, _pad_1206)] + prefix = 'R' + vias = [] + + +class C_1206(Component): + ''' 1206 Capacitor + ''' + pins = [Pin(-0.06, 0, _pad_1206), Pin(0.06, 0, _pad_1206)] + prefix = 'C' + vias = [] + +_pad_0805 = s2d.rectangle(-.023,.023, -.027, .027) + +class R_0805(Component): + ''' 0805 Resistor + ''' + pins = [Pin(-0.04, 0, _pad_0805), Pin(0.04, 0, _pad_0805)] + prefix = 'R' + vias = [] + + +class C_0805(Component): + ''' 0805 Capacitor + ''' + pins = [Pin(-0.04, 0, _pad_0805), Pin(0.04, 0, _pad_0805)] + prefix = 'C' + vias = [] + + +_pad_SJ = s2d.rectangle(-0.02, 0.02, -0.03, 0.03) +class SJ(Component): + ''' Solder jumper + ''' + pins = [Pin(-0.029, 0, _pad_SJ), Pin(0.029, 0, _pad_SJ)] + prefix = 'SJ' + vias = [] + +_pad_SOD_123 = s2d.rectangle(-0.02, 0.02, -0.024, 0.024) +class D_SOD_123(Component): + ''' Diode + ''' + pins = [Pin(-0.07, 0, _pad_SOD_123, 'A'), + Pin(0.07, 0, _pad_SOD_123, 'C')] + prefix = 'D' + vias = [] + + +################################################################################ +# Connectors +################################################################################ + +_pad_USB_trace = s2d.rectangle(-0.0075, 0.0075, -0.04, 0.04) +_pad_USB_foot = s2d.rectangle(-0.049, 0.049, -0.043, 0.043) +class USB_mini_B(Component): + ''' USB mini B connector + Hirose UX60-MB-5ST + ''' + pins = [ + Pin(0.063, 0.24, _pad_USB_trace, 'G'), + Pin(0.0315, 0.24, _pad_USB_trace), + Pin(0, 0.24, _pad_USB_trace, '+'), + Pin(-0.0315, 0.24, _pad_USB_trace, '-'), + Pin(-0.063, 0.24, _pad_USB_trace, 'V'), + + Pin( 0.165, 0.21, _pad_USB_foot), + Pin(-0.165, 0.21, _pad_USB_foot), + Pin( 0.165, 0.0, _pad_USB_foot), + Pin(-0.165, 0.0, _pad_USB_foot) + ] + prefix = 'J' + vias = [] + +_pad_header = s2d.rectangle(-0.06, 0.06, -0.025, 0.025) +_pad_header_skinny = s2d.rectangle(-0.06, 0.06, -0.020, 0.020) +class Header_4(Component): + ''' 4-pin header + fci 95278-101a04lf bergstik 2x2x0.1 + ''' + pins = [ + Pin(-0.107, 0.05, _pad_header), + Pin(-0.107, -0.05, _pad_header), + Pin( 0.107, -0.05, _pad_header), + Pin( 0.107, 0.05, _pad_header) + ] + prefix = 'J' + vias = [] + +class Header_4_skinny(Component): + ''' 4-pin header + fci 95278-101a04lf bergstik 2x2x0.1 + ''' + pins = [ + Pin(-0.107, 0.05, _pad_header_skinny), + Pin(-0.107, -0.05, _pad_header_skinny), + Pin( 0.107, -0.05, _pad_header_skinny), + Pin( 0.107, 0.05, _pad_header_skinny) + ] + prefix = 'J' + vias = [] + +class Header_Power(Component): + ''' 4-pin header + fci 95278-101a04lf bergstik 2x2x0.1 + ''' + pins = [ + Pin(-0.107, 0.05, _pad_header,"V"), + Pin(-0.107, -0.05, _pad_header,"GND"), + Pin( 0.107, -0.05, _pad_header), + Pin( 0.107, 0.05, _pad_header) + ] + prefix = 'J' + vias = [] + +class Header_ISP(Component): + ''' ISP programming header + FCI 95278-101A06LF Bergstik 2x3x0.1 + ''' + pins = [ + Pin(-0.107, 0.1, _pad_header, 'GND'), + Pin(-0.107, 0, _pad_header, 'MOSI'), + Pin(-0.107, -0.1, _pad_header, 'V'), + Pin( 0.107, -0.1, _pad_header, 'MISO'), + Pin( 0.107, 0, _pad_header, 'SCK'), + Pin( 0.107, 0.1, _pad_header, 'RST') + ] + prefix = 'J' + vias = [] + +class Header_ISP_skinny(Component): + ''' ISP programming header + FCI 95278-101A06LF Bergstik 2x3x0.1 + ''' + pins = [ + Pin(-0.107, 0.1, _pad_header_skinny, 'GND'), + Pin(-0.107, 0, _pad_header_skinny, 'MOSI'), + Pin(-0.107, -0.1, _pad_header_skinny, 'V'), + Pin( 0.107, -0.1, _pad_header_skinny, 'MISO'), + Pin( 0.107, 0, _pad_header_skinny, 'SCK'), + Pin( 0.107, 0.1, _pad_header_skinny, 'RST') + ] + prefix = 'J' + vias = [] + #shadow = s2d.rectangle(-.06,8/25.4,-.325,.325) + + +class Header_FTDI(Component): + ''' FTDI cable header + ''' + pins = [ + Pin(0, 0.25, _pad_header, 'GND'), + Pin(0, 0.15, _pad_header, 'CTS'), + Pin(0, 0.05, _pad_header, 'VCC'), + Pin(0, -0.05, _pad_header, 'TX'), + Pin(0, -0.15, _pad_header, 'RX'), + Pin(0, -0.25, _pad_header, 'RTS') + ] + prefix = 'J' + vias = [] + shadow = s2d.rectangle(-.06,8/25.4,-.325,.325) + +class Header_FTDI_skinny(Component): + ''' FTDI cable header + ''' + pins = [ + Pin(0, 0.25, _pad_header_skinny, 'GND'), + Pin(0, 0.15, _pad_header_skinny, 'CTS'), + Pin(0, 0.05, _pad_header_skinny, 'VCC'), + Pin(0, -0.05, _pad_header_skinny, 'TX'), + Pin(0, -0.15, _pad_header_skinny, 'RX'), + Pin(0, -0.25, _pad_header_skinny, 'RTS') + ] + prefix = 'J' + vias = [] + shadow = s2d.rectangle(-.06,8/25.4,-.325,.325) + + +class ScrewTerminal(Component): + pitch = .131 + _pad = s2d.rectangle(-0.04, 0.04, -0.04, 0.04) + _via = s2d.circle(0,0,.025) + pins = [Pin(-.5*pitch,0,_pad),Pin(.5*pitch,0,_pad)] + vias = [Via(-.5*pitch,0,_via),Via(.5*pitch,0,_via)] + shadow = s2d.rectangle(-3.5/25.4,3.5/25.4,-3/25.4,3/25.4) + +class ScrewTerminal3(Component): + pitch = .131 + _pad = s2d.rectangle(-0.04, 0.04, -0.04, 0.04) + _via = s2d.circle(0,0,.025) + pins = [Pin(-pitch,0,_pad),Pin(0,0,_pad),Pin(pitch,0,_pad)] + vias = [Via(-pitch,0,_via),Via(0,0,_via),Via(pitch,0,_via)] + shadow = s2d.rectangle(-5.35/25.4,5.35/25.4,-3/25.4,3/25.4) + +class JST_2(Component): + pitch = 2./25.4 + _pad = s2d.rectangle(-0.5/25.4,0.5/25.4, -1.75/25.4, 1.75/25.4) + _pad2 = s2d.rectangle(-.75/25.4,.75/25.4,-1.7/25.4,1.7/25.4) + y2 = -4.55/25.4 + pins = [Pin(-.5*pitch,0,_pad,'VCC'),Pin(.5*pitch,0,_pad,'GND'),Pin(-.5*pitch-2.35/25.4,y2,_pad2),Pin(.5*pitch+2.35/25.4,y2,_pad2)] + vias = [] + shadow = s2d.rectangle(-3.95/25.4,3.95/25.4,y2-1.7/25.4,1.75/25.4) + +################################################################################ +# SOT-23 components +################################################################################ + +_pad_SOT23 = s2d.rectangle(-.02,.02,-.012,.012) +class NMOS_SOT23(Component): + ''' NMOS transistor in SOT23 package + Fairchild NDS355AN + ''' + pins = [ + Pin(0.045, -0.0375, _pad_SOT23,'G'), + Pin(0.045, 0.0375, _pad_SOT23,'S'), + Pin(-0.045, 0, _pad_SOT23,'D') + ] + prefix = 'Q' + vias = [] + +class PMOS_SOT23(Component): + ''' PMOS transistor in SOT23 package + Fairchild NDS356AP + ''' + pins = [ + Pin(-0.045, -0.0375, _pad_SOT23,'G'), + Pin(-0.045, 0.0375, _pad_SOT23,'S'), + Pin(0.045, 0, _pad_SOT23,'D') + ] + prefix = 'Q' + vias = [] + +class Regulator_SOT23(Component): + ''' SOT23 voltage regulator + ''' + pins = [ + Pin(-0.045, -0.0375, _pad_SOT23,'Out'), + Pin(-0.045, 0.0375, _pad_SOT23,'In'), + Pin(0.045, 0, _pad_SOT23,'GND') + ] + prefix = 'U' + vias = [] + +class Regulator_LM3480(Component): + ''' SOT23 voltage regulator, LM3480 + ''' + pins = [ + Pin(-0.045, -0.0375, _pad_SOT23,'In'), + Pin(-0.045, 0.0375, _pad_SOT23,'Out'), + Pin(0.045, 0, _pad_SOT23,'GND') + ] + prefix = 'U' + vias = [] + +########### +# H Bridge +############ +_pad_SOIC = s2d.rectangle(-.041,.041,-.015,.015) +class A4953_SOICN(Component): + pins = [ + Pin(-.11, .075,_pad_SOIC+s2d.circle(-.041,0,.015),"GND"), + Pin(-.11, .025,_pad_SOIC,"IN2"), + Pin(-.11,-.025,_pad_SOIC,"IN1"), + Pin(-.11,-.075,_pad_SOIC,"VREF"), + Pin( .11,-.075,_pad_SOIC,"VBB"), + Pin( .11,-.025,_pad_SOIC,"OUT1"), + Pin( .11, .025,_pad_SOIC,"LSS"), + Pin( .11, .075,_pad_SOIC,"OUT2"), + Pin( 0,0,s2d.rectangle(-.04,.04,-.075,.075),"") + ] + prefix = 'U' + vias = [] + + +################################################################################ +# Clock crystals +################################################################################ +_pad_XTAL_NX5032GA = s2d.rectangle(-.039,.039,-.047,.047) + +class XTAL_NX5032GA(Component): + pins = [Pin(-0.079, 0, _pad_XTAL_NX5032GA), + Pin(0.079, 0, _pad_XTAL_NX5032GA)] + prefix = 'X' + vias = [] + +################################################################################ +# Atmel microcontrollers +################################################################################ + +_pad_SOIC = s2d.rectangle(-0.041, 0.041, -0.015, 0.015) +class ATtiny45_SOIC(Component): + pins = [] + y = 0.075 + for t in ['NC', 'PB3', 'PB4', 'GND']: + pins.append(Pin(-0.14, y, _pad_SOIC, t)) + y -= 0.05 + for p in ['PB0', 'PB1', 'PB2', 'VCC']: + y += 0.05 + pins.append(Pin(0.14, y, _pad_SOIC, p)) + del y + prefix = 'U' + vias = [] + +class ATtiny44_SOIC(Component): + pins = [] + y = 0.15 + for t in ['VCC', 'PB0', 'PB1', 'PB3', 'PB2', 'PA7', 'PA6']: + pad = _pad_SOIC + s2d.circle(-0.041, 0, 0.015) if t == 'VCC' else _pad_SOIC + pins.append(Pin(-0.12, y, pad, t)) + y -= 0.05 + for t in ['PA5', 'PA4', 'PA3', 'PA2', 'PA1', 'PA0', 'GND']: + y += 0.05 + pins.append(Pin(0.12, y, _pad_SOIC, t)) + prefix = 'U' + vias = [] + +_pad_TQFP_h = s2d.rectangle(-0.025, 0.025, -0.008, 0.008) +_pad_TQFP_v = s2d.rectangle(-0.008, 0.008, -0.025, 0.025) + +class ATmega88_TQFP(Component): + pins = [] + y = 0.1085 + for t in ['PD3', 'PD4', 'GND', 'VCC', 'GND', 'VCC', 'PB6', 'PB7']: + pins.append(Pin(-0.18, y, _pad_TQFP_h, t)) + y -= 0.031 + x = -0.1085 + for t in ['PD5', 'PD6', 'PD7', 'PB0', 'PB1', 'PB2', 'PB3', 'PB4']: + pins.append(Pin(x, -0.18, _pad_TQFP_v, t)) + x += 0.031 + y = -0.1085 + for t in ['PB5', 'AVCC', 'ADC6', 'AREF', 'GND', 'ADC7', 'PC0', 'PC1']: + pins.append(Pin(0.18, y, _pad_TQFP_h, t)) + y += 0.031 + x = 0.1085 + for t in ['PC2', 'PC3', 'PC4', 'PC5', 'PC6', 'PD0', 'PD1', 'PD2']: + pins.append(Pin(x, 0.18, _pad_TQFP_v, t)) + x -= 0.031 + del x, y + prefix = 'U' + vias = [] + + +################################################################################ +# CBA logo +################################################################################ +_pin_circle_CBA = s2d.circle(0, 0, 0.02) +_pin_square_CBA = s2d.rectangle(-0.02, 0.02, -0.02, 0.02) +class CBA(Component): + pins = [] + for i in range(3): + for j in range(3): + pin = _pin_circle_CBA if i == 2-j and j >= 1 else _pin_square_CBA + pins.append(Pin(0.06*(i-1), 0.06*(j-1), pin)) + vias = [] + + + +class ESP8266_03(Component): + _pad = s2d.rectangle(-0.04, 0.04, -0.03, 0.03) + _via = s2d.circle(0,0,.019) + names = ['VCC','GPIO14','GPIO12','GPIO13','GPIO15','GPIO2','GPIO0', + 'WIFI_ANT','CH-PD','GPIO18','URXD','UTXD','NC','GND'] + w = 12.2/25.4 + l = 17.4/25.4 + wp = 12.2/25.4 + lp = .5 + dp = 2/25.4 + ys = arange(.5*lp-dp ,-.5*lp-.001-dp,-dp) + pts = vstack(( dstack((-.5*wp*ones_like(ys),ys))[0], dstack((.5*wp*ones_like(ys),ys))[0] )) + pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)] + vias = []#[Via(p[0],p[1],_via) for n,p in zip(names,pts)] + shadow = s2d.rectangle(-.5*w,.5*w,-.5*l,.5*l) + prefix = 'IC' + +class ZLDO1117(Component): + '''3.3 V 1 A regulator, SOT223''' + _pad1 = s2d.rectangle(-.6/25.4,.6/25.4,-.8/25.4,.8/25.4) + _pad2 = s2d.rectangle(-1.65/25.4,1.65/25.4,-.6/25.4,.6/25.4) + pins = [ + Pin(-2.3/25.4, -3.2/25.4, _pad1,'GND'), + Pin(0, -3.2/25.4, _pad1,'Vout'), + Pin(2.3/25.4, -3.2/25.4, _pad1,'Vin'), + Pin(0, 3.2/25.4, _pad2,'Vout2'), + ] + prefix = 'U' + vias = [] + +class AstarMicro(Component): + ''' Polulo Astar micro + ''' + _pad = s2d.rectangle(-0.04, 0.04, -0.025, 0.025) + _via = s2d.circle(0,0,.019) + #flip names since through hole + names = [ + 'VIN','GND','5V','3v3','RST','12/A11/PWM','11','10/A10/PWM','A1','A0', + '9/A9/PWM','8/A8','7','6/A7/PWM','5/PWM','4/A6','3/PWM','2','1','0'] + w = .6 + l = 1. + wp = .5 + lp = .9 + ys = arange(.5*lp,-.5*lp-.001,-.1) + os = 0*.13*(arange(shape(ys)[0])%2-.5) + pts = vstack(( dstack((-.5*wp*ones_like(ys)+os,ys[::-1]))[0], dstack((.5*wp*ones_like(ys)-os,ys))[0] )) + pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)] + vias = [Via(p[0],p[1],_via) for n,p in zip(names,pts)] + shadow = s2d.rectangle(-.5*w,.5*w,-.5*l,.5*l) + prefix = 'IC' + +class Header_bldc_skinny(Component): + ''' brushless motor logic + ''' + _pad_header_skinny = s2d.rectangle(-0.06, 0.06, -0.020, 0.020) + pins = [ + Pin(0, 0.1, _pad_header_skinny, 'GND'), + Pin(0, -0.0, _pad_header_skinny, 'VCC'), + Pin(0, -0.1, _pad_header_skinny, 'RC') + ] + prefix = 'J' + shadow = s2d.rectangle(-.06,8/25.4,-.325,.325) + vias = [] + + +class A4988_Carrier(Component): + ''' Stepper driver carrier black from pololu + ''' + _pad = s2d.rectangle(-0.04, 0.04, -0.028, 0.028) + _via = s2d.circle(0,0,.019) + names = ['VMOT','GMOT','2B','2A','1A','1B','VDD','GND','DIR','STEP','SLP','RST','MS3','MS2','MS1','EN'] + ys = arange(.4,-.4+.001,-.1)-.05 + pts = vstack(( dstack((-.25*ones_like(ys),ys))[0], dstack((.25*ones_like(ys),ys[::-1]))[0] )) + pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)] + vias = [Via(p[0],p[1],_via) for n,p in zip(names,pts)] + prefix = 'IC' + shadow = s2d.rectangle(-.3,.3,-.45,.45) + + + + +class CDRH2D18(Component): + '''Power Inductor''' + def chamfered_rectangle(x0,x1,y0,y1,c): + r = s2d.rectangle(x0,x1,y0,y1) + c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0) + c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1) + c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c) + c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c) + return r-c1-c2-c3-c4 + _pad = s2d.rectangle(-.65/25.4,.65/25.4,-.65/25.4,.65/25.4) + pins = [Pin(-1.5/25.5,0,_pad), Pin(1.5/25.5,0,_pad)] + vias = [] + shadow = s2d.rotate(chamfered_rectangle(-1.5/25.4,1.5/25.4,-1.5/25.4, 1.5/25.4,1/25.5),45) + prefix='I' + +class LTC35881(Component): + ''' Energy Scavenger ''' + _pad = s2d.rectangle(-.889/2/25.4, .889/2/25.4,-.25/2/25.4, .25/2/25.4) + p = .5/25.4 + pins = [ + Pin(0, 0, s2d.rectangle(-1.68/2/25.4,1.68/2/25.4,-1.88/2/25.4,1.88/2/25.4), 'GND'), + Pin(-2.1/25.4, 2*p,_pad,'PZ1',label_size=.015,label_rot=0), + Pin(-2.1/25.4, 1*p,_pad,'PZ2',label_size=.015,label_rot=0), + Pin(-2.1/25.4, 0,_pad,'CAP',label_size=.015,label_rot=0), + Pin(-2.1/25.4,-1*p,_pad,'VIN',label_size=.015,label_rot=0), + Pin(-2.1/25.4,-2*p,_pad,'SW',label_size=.015,label_rot=0), + Pin(2.1/25.4, -2*p,_pad,'VOUT',label_size=.015,label_rot=0), + Pin(2.1/25.4, -1*p,_pad,'VIN2',label_size=.015,label_rot=0), + Pin(2.1/25.4, 0,_pad,'D1',label_size=.015,label_rot=0), + Pin(2.1/25.4, 1*p,_pad,'D0',label_size=.015,label_rot=0), + Pin(2.1/25.4, 2*p,_pad,'PGOOD',label_size=.015,label_rot=0) + ] + prefix = 'J' + h = 2.9/25.4; w = 2.8/25.4; + shadow = s2d.rectangle(-.5*w,.5*w,-.5*h,.5*h) + vias = [] + +class DSK414(Component): + '''Dynacap, ELNA, 220mF''' + pins = [ + Pin(0,5.15/25.4, s2d.rectangle(-2.4/25.4,2.4/25.4,-1./25.4,1/25.4),'+'), + Pin(0,-5/25.4, s2d.rectangle(-2/25.4,2/25.4,-.85/25.4,.81/25.4),'-') + ] + vias = [] + shadow = s2d.rectangle(-2.5/25.4,2.5/25.4,-5.85/25.4, 6.15/25.4) + shadow += s2d.circle(0,0,3.4/25.4) + prefix='C' + +class EECRG(Component): + '''Panasonic 1F, 3.6 V''' + _pad = s2d.rectangle(-.02,.02,-.035,.035) + pins = [ + Pin(-10/25.4, 0, _pad), + Pin(10/25.4, 0, _pad) + ] + _via = s2d.rectangle(-.1/25.4,.1/25.4,-.5/25.4,.5/25.4) + vias = [Via(p.x,p.y,_via) for p in pins] + shadow = s2d.rectangle(0,0,0,0) + prefix='C' + +class EEE1EA101XP(Component): + '''Panasonic 100uF, 25V''' + _pad = s2d.rectangle(-.6/25.4,.6/25.4,-1.35/25.4,1.35/25.4) + pins = [ + Pin(0, 2.2/25.4, _pad), + Pin(0, -2.2/25.4, _pad) + ] + #_via = s2d.rectangle(-.1/25.4,.1/25.4,-.5/25.4,.5/25.4) + vias = [] + def half_chamfered_rectangle(x0,x1,y0,y1,c): + r = s2d.rectangle(x0,x1,y0,y1) + c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0) + c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1) + c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c) + c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c) + return r-c1-c4 + shadow = half_chamfered_rectangle(-3.3/25.4,3.3/25.4,-3.3/25.4,3.3/25.4,1/25.4) + prefix='C' + + +def chamfered_rectangle(x0,x1,y0,y1,c): + r = s2d.rectangle(x0,x1,y0,y1) + c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0) + c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1) + c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c) + c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c) + return r-c1-c2-c3-c4 +