def __createFootprintVariant(self, device_params, device_dimensions, with_thermal_vias): fab_line_width = self.configuration.get('fab_line_width', 0.1) silk_line_width = self.configuration.get('silk_line_width', 0.12) lib_name = device_params.get('library', default_library) pincount = device_params['num_pins_x'] * 2 + device_params[ 'num_pins_y'] * 2 default_ipc_config = 'qfn_pull_back' if 'lead_to_edge' in device_params else 'qfn' if device_params.get('ipc_class', default_ipc_config) == 'qfn_pull_back': ipc_reference = 'ipc_spec_flat_no_lead_pull_back' else: ipc_reference = 'ipc_spec_flat_no_lead' used_density = device_params.get('ipc_density', ipc_density) ipc_data_set = self.ipc_defintions[ipc_reference][used_density] ipc_round_base = self.ipc_defintions[ipc_reference]['round_base'] layout = '' if device_dimensions['has_EP']: name_format = self.configuration[ 'fp_name_EP_format_string_no_trailing_zero'] if 'EP_size_x_overwrite' in device_params: EP_size = { 'x': device_params['EP_size_x_overwrite'], 'y': device_params['EP_size_y_overwrite'] } else: EP_size = { 'x': device_dimensions['EP_size_x'].nominal, 'y': device_dimensions['EP_size_y'].nominal } EP_center = { 'x': device_dimensions['EP_center_x'].nominal, 'y': device_dimensions['EP_center_y'].nominal } else: name_format = self.configuration[ 'fp_name_format_string_no_trailing_zero'] if device_params.get('use_name_format', 'QFN') == 'LGA': name_format = self.configuration[ 'fp_name_lga_format_string_no_trailing_zero'] if device_params['num_pins_x'] > 0 and device_params[ 'num_pins_y'] > 0: layout = self.configuration['lga_layout_border'].format( nx=device_params['num_pins_x'], ny=device_params['num_pins_y']) EP_size = {'x': 0, 'y': 0} if 'custom_name_format' in device_params: name_format = device_params['custom_name_format'] pad_details = self.calcPadDetails(device_dimensions, EP_size, ipc_data_set, ipc_round_base) pad_suffix = '_Pad{pad_x:.2f}x{pad_y:.2f}mm'.format( pad_x=pad_details['left']['size'][0], pad_y=pad_details['left']['size'][1]) pad_suffix = '' if device_params.get( 'include_pad_size', 'none') not in ('fp_name_only', 'both') else pad_suffix pad_suffix_3d = '' if device_params.get( 'include_pad_size', 'none') not in ('both') else pad_suffix suffix = device_params.get('suffix', '') suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else "" model3d_path_prefix = self.configuration.get('3d_model_prefix', '${KISYS3DMOD}') size_x = device_dimensions['body_size_x'].nominal size_y = device_dimensions['body_size_y'].nominal fp_name = name_format.format( man=device_params.get('manufacturer', ''), mpn=device_params.get('part_number', ''), pkg=device_params['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_dimensions['pitch'], layout=layout, ep_size_x=EP_size['x'], ep_size_y=EP_size['y'], suffix=pad_suffix, suffix2=suffix, vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else '').replace('__', '_').lstrip('_') fp_name_2 = name_format.format( man=device_params.get('manufacturer', ''), mpn=device_params.get('part_number', ''), pkg=device_params['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_dimensions['pitch'], layout=layout, ep_size_x=EP_size['x'], ep_size_y=EP_size['y'], suffix=pad_suffix_3d, suffix2=suffix_3d, vias='').replace('__', '_').lstrip('_') if 'fp_name_prefix' in device_params: prefix = device_params['fp_name_prefix'] if not prefix.endswith('_'): prefix += '_' fp_name = prefix + fp_name fp_name_2 = prefix + fp_name_2 model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\ .format( model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=fp_name_2) #print(fp_name) #print(pad_details) kicad_mod = Footprint(fp_name) # init kicad footprint kicad_mod.setDescription( "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\ .format( manufacturer = device_params.get('manufacturer',''), package = device_params['device_type'], mpn = device_params.get('part_number',''), pincount = pincount, datasheet = device_params['size_source'], scriptname = os.path.basename(__file__).replace(" ", " ") ).lstrip()) kicad_mod.setTags(self.configuration['keyword_fp_string']\ .format( man=device_params.get('manufacturer',''), package=device_params['device_type'], category=category ).lstrip()) kicad_mod.setAttribute('smd') pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get( 'round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration[ 'round_rect_max_radius'] if device_dimensions['has_EP']: if with_thermal_vias: thermals = device_params['thermal_vias'] paste_coverage = thermals.get( 'EP_paste_coverage', device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE)) kicad_mod.append( ExposedPad( number=pincount + 1, size=EP_size, at=EP_center, paste_layout=thermals.get( 'EP_num_paste_pads', device_params.get('EP_num_paste_pads', 1)), paste_coverage=paste_coverage, via_layout=thermals.get('count', 0), paste_between_vias=thermals.get('paste_between_vias'), paste_rings_outside=thermals.get( 'paste_rings_outside'), via_drill=thermals.get('drill', 0.3), via_grid=thermals.get('grid'), paste_avoid_via=thermals.get('paste_avoid_via', True), via_paste_clarance=thermals.get( 'paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE), min_annular_ring=thermals.get( 'min_annular_ring', DEFAULT_MIN_ANNULAR_RING), bottom_pad_min_size=thermals.get('bottom_min_size', 0), kicad4_compatible=args.kicad4_compatible, **pad_shape_details)) else: kicad_mod.append( ExposedPad(number=pincount + 1, size=EP_size, at=EP_center, paste_layout=device_params.get( 'EP_num_paste_pads', 1), paste_coverage=device_params.get( 'EP_paste_coverage', DEFAULT_PASTE_COVERAGE), kicad4_compatible=args.kicad4_compatible, **pad_shape_details)) add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) body_edge = { 'left': -size_x / 2, 'right': size_x / 2, 'top': -size_y / 2, 'bottom': size_y / 2 } bounding_box = body_edge.copy() if device_params['num_pins_x'] == 0 and EP_size['y'] > size_y: bounding_box['top'] = -EP_size['y'] / 2 bounding_box['bottom'] = EP_size['y'] / 2 if device_params['num_pins_y'] == 0 and EP_size['x'] > size_x: bounding_box['left'] = -EP_size['x'] / 2 bounding_box['right'] = EP_size['x'] / 2 if device_params['num_pins_y'] > 0: bounding_box['left'] = pad_details['left']['center'][ 0] - pad_details['left']['size'][0] / 2 bounding_box['right'] = pad_details['right']['center'][ 0] + pad_details['right']['size'][0] / 2 if device_params['num_pins_x'] > 0: bounding_box['top'] = pad_details['top']['center'][ 1] - pad_details['top']['size'][1] / 2 bounding_box['bottom'] = pad_details['bottom']['center'][ 1] + pad_details['bottom']['size'][1] / 2 pad_width = pad_details['top']['size'][0] # ############################ SilkS ################################## silk_pad_offset = configuration[ 'silk_pad_clearance'] + configuration['silk_line_width'] / 2 silk_offset = configuration['silk_fab_offset'] if device_params['num_pins_x'] == 0: kicad_mod.append( Line(start={ 'x': 0, 'y': body_edge['top'] - silk_offset }, end={ 'x': body_edge['right'], 'y': body_edge['top'] - silk_offset }, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append( Line(start={ 'x': body_edge['left'], 'y': body_edge['bottom'] + silk_offset }, end={ 'x': body_edge['right'], 'y': body_edge['bottom'] + silk_offset }, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) elif device_params['num_pins_y'] == 0: kicad_mod.append( Line(start={ 'y': 0, 'x': body_edge['left'] - silk_offset }, end={ 'y': body_edge['bottom'], 'x': body_edge['left'] - silk_offset }, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append( Line(start={ 'y': body_edge['top'], 'x': body_edge['right'] + silk_offset }, end={ 'y': body_edge['bottom'], 'x': body_edge['right'] + silk_offset }, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) else: sx1 = -(device_dimensions['pitch'] * (device_params['num_pins_x'] - 1) / 2.0 + pad_width / 2.0 + silk_pad_offset) sy1 = -(device_dimensions['pitch'] * (device_params['num_pins_y'] - 1) / 2.0 + pad_width / 2.0 + silk_pad_offset) poly_silk = [{ 'x': sx1, 'y': body_edge['top'] - silk_offset }, { 'x': body_edge['left'] - silk_offset, 'y': body_edge['top'] - silk_offset }, { 'x': body_edge['left'] - silk_offset, 'y': sy1 }] if sx1 - SILK_MIN_LEN < body_edge['left'] - silk_offset: poly_silk = poly_silk[1:] if sy1 - SILK_MIN_LEN < body_edge['top'] - silk_offset: poly_silk = poly_silk[:-1] if len(poly_silk) > 1: kicad_mod.append( PolygoneLine(polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) kicad_mod.append( PolygoneLine(polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) kicad_mod.append( PolygoneLine(polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0, y_mirror=0)) if len(poly_silk) > 2: kicad_mod.append( Line(start={ 'x': sx1, 'y': body_edge['top'] - silk_offset }, end={ 'x': body_edge['left'] - silk_offset, 'y': body_edge['top'] - silk_offset }, width=configuration['silk_line_width'], layer="F.SilkS")) # # ######################## Fabrication Layer ########################### fab_bevel_size = min( configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative'] * min(size_x, size_y)) poly_fab = [ { 'x': body_edge['left'] + fab_bevel_size, 'y': body_edge['top'] }, { 'x': body_edge['right'], 'y': body_edge['top'] }, { 'x': body_edge['right'], 'y': body_edge['bottom'] }, { 'x': body_edge['left'], 'y': body_edge['bottom'] }, { 'x': body_edge['left'], 'y': body_edge['top'] + fab_bevel_size }, { 'x': body_edge['left'] + fab_bevel_size, 'y': body_edge['top'] }, ] kicad_mod.append( PolygoneLine(polygone=poly_fab, width=configuration['fab_line_width'], layer="F.Fab")) # # ############################ CrtYd ################################## off = ipc_data_set['courtyard'] grid = configuration['courtyard_grid'] cy1 = roundToBase(bounding_box['top'] - off, grid) kicad_mod.append( RectLine(start={ 'x': roundToBase(bounding_box['left'] - off, grid), 'y': cy1 }, end={ 'x': roundToBase(bounding_box['right'] + off, grid), 'y': roundToBase(bounding_box['bottom'] + off, grid) }, width=configuration['courtyard_line_width'], layer='F.CrtYd')) # ######################### Text Fields ############################### addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge, courtyard={ 'top': cy1, 'bottom': -cy1 }, fp_name=fp_name, text_y_inside_position='center') ##################### Output and 3d model ############################ kicad_mod.append(Model(filename=model_name)) output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name) if not os.path.isdir( output_dir ): #returns false if path does not yet exist!! (Does not check path validity) os.makedirs(output_dir) filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name) file_handler = KicadFileHandler(kicad_mod) file_handler.writeFile(filename)
def __createFootprintVariant(self, device_params, header, dimensions, with_thermal_vias): fab_line_width = self.configuration.get('fab_line_width', 0.1) silk_line_width = self.configuration.get('silk_line_width', 0.12) lib_name = self.configuration['lib_name_format_string'].format(category=header['library_Suffix']) size_x = dimensions['body_size_x'].nominal size_y = dimensions['body_size_y'].nominal pincount = device_params['num_pins_x']*2 + device_params['num_pins_y']*2 ipc_reference = 'ipc_spec_gw_large_pitch' if device_params['pitch'] >= 0.625 else 'ipc_spec_gw_small_pitch' if device_params.get('force_small_pitch_ipc_definition', False): ipc_reference = 'ipc_spec_gw_small_pitch' used_density = device_params.get('ipc_density', ipc_density) ipc_data_set = self.ipc_defintions[ipc_reference][used_density] ipc_round_base = self.ipc_defintions[ipc_reference]['round_base'] pitch = device_params['pitch'] name_format = self.configuration['fp_name_format_string_no_trailing_zero'] EP_size = {'x':0, 'y':0} EP_mask_size = {'x':0, 'y':0} if dimensions['has_EP']: name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero'] if 'EP_size_x_overwrite' in device_params: EP_size = { 'x':device_params['EP_size_x_overwrite'], 'y':device_params['EP_size_y_overwrite'] } else: EP_size = { 'x':dimensions['EP_size_x'].nominal, 'y':dimensions['EP_size_y'].nominal } if 'EP_mask_x' in dimensions: name_format = self.configuration['fp_name_EP_custom_mask_format_string_no_trailing_zero'] EP_mask_size = {'x':dimensions['EP_mask_x'].nominal, 'y':dimensions['EP_mask_y'].nominal} EP_size = Vector2D(EP_size) pad_details = self.calcPadDetails(dimensions, EP_size, ipc_data_set, ipc_round_base) if 'custom_name_format' in device_params: name_format = device_params['custom_name_format'] suffix = device_params.get('suffix', '').format(pad_x=pad_details['left']['size'][0], pad_y=pad_details['left']['size'][1]) suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else "" model3d_path_prefix = self.configuration.get('3d_model_prefix','${KISYS3DMOD}') fp_name = name_format.format( man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=header['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], ep_size_x = EP_size['x'], ep_size_y = EP_size['y'], mask_size_x = EP_mask_size['x'], mask_size_y = EP_mask_size['y'], suffix=suffix, suffix2="", vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else '' ).replace('__','_').lstrip('_') fp_name_2 = name_format.format( man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=header['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], ep_size_x = EP_size['x'], ep_size_y = EP_size['y'], mask_size_x = EP_mask_size['x'], mask_size_y = EP_mask_size['y'], suffix=suffix_3d, suffix2="", vias='' ).replace('__','_').lstrip('_') model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\ .format( model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=fp_name_2) #print(fp_name) #print(pad_details) kicad_mod = Footprint(fp_name) # init kicad footprint kicad_mod.setDescription( "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\ .format( manufacturer = device_params.get('manufacturer',''), package = header['device_type'], mpn = device_params.get('part_number',''), pincount = pincount, datasheet = device_params['size_source'], scriptname = os.path.basename(__file__).replace(" ", " ") ).lstrip()) kicad_mod.setTags(self.configuration['keyword_fp_string']\ .format( man=device_params.get('manufacturer',''), package=header['device_type'], category=header['library_Suffix'] ).lstrip()) kicad_mod.setAttribute('smd') pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] EP_round_radius = 0 if dimensions['has_EP']: EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None if with_thermal_vias: thermals = device_params['thermal_vias'] paste_coverage = thermals.get('EP_paste_coverage', device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE)) EP = ExposedPad( number=pincount+1, size=EP_size, mask_size=EP_mask_size, paste_layout=thermals.get('EP_num_paste_pads'), paste_coverage=paste_coverage, via_layout=thermals.get('count', 0), paste_between_vias=thermals.get('paste_between_vias'), paste_rings_outside=thermals.get('paste_rings_outside'), via_drill=thermals.get('drill', 0.3), via_grid=thermals.get('grid'), paste_avoid_via=thermals.get('paste_avoid_via', True), via_paste_clarance=thermals.get('paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE), min_annular_ring=thermals.get('min_annular_ring', DEFAULT_MIN_ANNULAR_RING), bottom_pad_min_size=thermals.get('bottom_min_size', 0), **pad_shape_details ) else: EP = ExposedPad( number=pincount+1, size=EP_size, mask_size=EP_mask_size, paste_layout=device_params.get('EP_num_paste_pads', 1), paste_coverage=device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE), **pad_shape_details ) kicad_mod.append(EP) EP_round_radius = EP.getRoundRadius() pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) body_edge = { 'left': -dimensions['body_size_x'].nominal/2, 'right': dimensions['body_size_x'].nominal/2, 'top': -dimensions['body_size_y'].nominal/2, 'bottom': dimensions['body_size_y'].nominal/2 } bounding_box = { 'left': pad_details['left']['center'][0] - pad_details['left']['size'][0]/2, 'right': pad_details['right']['center'][0] + pad_details['right']['size'][0]/2, 'top': pad_details['top']['center'][1] - pad_details['top']['size'][1]/2, 'bottom': pad_details['bottom']['center'][1] + pad_details['bottom']['size'][1]/2 } if device_params['num_pins_x'] == 0: bounding_box['top'] = body_edge['top'] bounding_box['bottom'] = body_edge['bottom'] if EP_size['y'] > dimensions['body_size_y'].nominal: bounding_box['top'] = -EP_size['y']/2 bounding_box['bottom'] = EP_size['y']/2 if device_params['num_pins_y'] == 0: bounding_box['left'] = body_edge['left'] bounding_box['right'] = body_edge['right'] if EP_size['x'] > dimensions['body_size_x'].nominal: bounding_box['left'] = -EP_size['x']/2 bounding_box['right'] = EP_size['x']/2 pad_width = pad_details['top']['size'][0] # ############################ SilkS ################################## silk_pad_offset = configuration['silk_pad_clearance'] + configuration['silk_line_width']/2 silk_offset = configuration['silk_fab_offset'] right_pads_silk_bottom = (device_params['num_pins_y']-1)*device_params['pitch']/2\ +pad_details['right']['size'][1]/2+silk_pad_offset silk_bottom = body_edge['bottom']+silk_offset if EP_size['y']/2 <= body_edge['bottom'] and right_pads_silk_bottom >= silk_bottom: silk_bottom = max(silk_bottom, EP_size['y']/2+silk_pad_offset) silk_bottom = max(silk_bottom, right_pads_silk_bottom) silk_bottom = min(body_edge['bottom']+silk_pad_offset, silk_bottom) bottom_pads_silk_right = (device_params['num_pins_x']-1)*device_params['pitch']/2\ +pad_details['bottom']['size'][0]/2+silk_pad_offset silk_right = body_edge['right']+silk_offset if EP_size['x']/2 <= body_edge['right'] and bottom_pads_silk_right >= silk_right: silk_right = max(silk_right, EP_size['x']/2+silk_pad_offset) silk_right = max(silk_right, bottom_pads_silk_right) silk_right = min(body_edge['right']+silk_pad_offset, silk_right) min_lenght = configuration.get('silk_line_lenght_min', 0) silk_corner_bottom_right = Vector2D(silk_right, silk_bottom) silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine( pad_size=EP_size, pad_position=[0, 0], pad_radius=EP_round_radius, fixed_point=silk_corner_bottom_right, moving_point=Vector2D(0, silk_bottom), silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_bottom_inside is not None and device_params['num_pins_x'] > 0: silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['bottom']['size'], pad_position=[ pad_details['bottom']['center'][0]+(device_params['num_pins_x']-1)/2*pitch, pad_details['bottom']['center'][1]], pad_radius=pad_radius, fixed_point=silk_corner_bottom_right, moving_point=silk_point_bottom_inside, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) silk_point_right_inside = nearestSilkPointOnOrthogonalLine( pad_size=EP_size, pad_position=[0, 0], pad_radius=EP_round_radius, fixed_point=silk_corner_bottom_right, moving_point=Vector2D(silk_right, 0), silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_right_inside is not None and device_params['num_pins_y'] > 0: silk_point_right_inside = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['right']['size'], pad_position=[ pad_details['right']['center'][0], pad_details['right']['center'][1]+(device_params['num_pins_y']-1)/2*pitch], pad_radius=pad_radius, fixed_point=silk_corner_bottom_right, moving_point=silk_point_right_inside, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_bottom_inside is None and silk_point_right_inside is not None: silk_corner_bottom_right['y'] = body_edge['bottom'] silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['bottom']['size'], pad_position=[ pad_details['bottom']['center'][0]+(device_params['num_pins_x']-1)/2*pitch, pad_details['bottom']['center'][1]], pad_radius=pad_radius, fixed_point=silk_point_right_inside, moving_point=silk_corner_bottom_right, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) elif silk_point_right_inside is None and silk_point_bottom_inside is not None: silk_corner_bottom_right['x'] = body_edge['right'] silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['right']['size'], pad_position=[ pad_details['right']['center'][0], pad_details['right']['center'][1]+(device_params['num_pins_y']-1)/2*pitch], pad_radius=pad_radius, fixed_point=silk_point_bottom_inside, moving_point=silk_corner_bottom_right, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) poly_bottom_right = [] if silk_point_bottom_inside is not None: poly_bottom_right.append(silk_point_bottom_inside) poly_bottom_right.append(silk_corner_bottom_right) if silk_point_right_inside is not None: poly_bottom_right.append(silk_point_right_inside) if len(poly_bottom_right) > 1 and silk_corner_bottom_right is not None: kicad_mod.append(PolygoneLine( polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append(PolygoneLine( polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) kicad_mod.append(PolygoneLine( polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) if device_params['num_pins_y'] > 0: if len(poly_bottom_right)>2: kicad_mod.append(PolygoneLine( polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0, x_mirror=0)) kicad_mod.append(Line( start={'x': -silk_right, 'y': -right_pads_silk_bottom}, end={'x': bounding_box['left'], 'y': -right_pads_silk_bottom}, width=configuration['silk_line_width'], layer="F.SilkS")) elif silk_corner_bottom_right['y'] >= right_pads_silk_bottom and silk_point_bottom_inside is not None: kicad_mod.append(Line( start=-silk_point_bottom_inside, end={'x': bounding_box['left'], 'y': -silk_point_bottom_inside['y']}, width=configuration['silk_line_width'], layer="F.SilkS")) else: if len(poly_bottom_right)>2: poly_bottom_right[0]['x']=bottom_pads_silk_right kicad_mod.append(PolygoneLine( polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0, x_mirror=0)) kicad_mod.append(Line( start={'x': -bottom_pads_silk_right, 'y': -silk_corner_bottom_right['y']}, end={'x': -bottom_pads_silk_right, 'y': bounding_box['top']}, width=configuration['silk_line_width'], layer="F.SilkS")) elif silk_corner_bottom_right['x'] >= bottom_pads_silk_right and silk_point_right_inside is not None: kicad_mod.append(Line( start=-silk_point_right_inside, end={'x': -silk_point_right_inside['x'], 'y': bounding_box['top']}, width=configuration['silk_line_width'], layer="F.SilkS")) # # ######################## Fabrication Layer ########################### fab_bevel_size = min(configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative']*min(size_x, size_y)) poly_fab = [ {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']}, {'x': body_edge['right'], 'y': body_edge['top']}, {'x': body_edge['right'], 'y': body_edge['bottom']}, {'x': body_edge['left'], 'y': body_edge['bottom']}, {'x': body_edge['left'], 'y': body_edge['top']+fab_bevel_size}, {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']}, ] kicad_mod.append(PolygoneLine( polygone=poly_fab, width=configuration['fab_line_width'], layer="F.Fab")) # # ############################ CrtYd ################################## off = ipc_data_set['courtyard'] grid = configuration['courtyard_grid'] if device_params['num_pins_y'] == 0 or device_params['num_pins_x'] == 0: cy1=roundToBase(bounding_box['top']-off, grid) kicad_mod.append(RectLine( start={ 'x':roundToBase(bounding_box['left']-off, grid), 'y':cy1 }, end={ 'x':roundToBase(bounding_box['right']+off, grid), 'y':roundToBase(bounding_box['bottom']+off, grid) }, width=configuration['courtyard_line_width'], layer='F.CrtYd')) else: cy1=roundToBase(bounding_box['top']-off, grid) cy2=roundToBase(body_edge['top']-off, grid) cy3=-roundToBase( device_params['pitch']*(device_params['num_pins_y']-1)/2.0 + pad_width/2.0 + off, grid) cx1=-roundToBase( device_params['pitch']*(device_params['num_pins_x']-1)/2.0 + pad_width/2.0 + off, grid) cx2=roundToBase(body_edge['left']-off, grid) cx3=roundToBase(bounding_box['left']-off, grid) crty_poly_tl = [ {'x':0, 'y':cy1}, {'x':cx1, 'y':cy1}, {'x':cx1, 'y':cy2}, {'x':cx2, 'y':cy2}, {'x':cx2, 'y':cy3}, {'x':cx3, 'y':cy3}, {'x':cx3, 'y':0} ] kicad_mod.append(PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'])) kicad_mod.append(PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], x_mirror=0)) kicad_mod.append(PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], y_mirror=0)) kicad_mod.append(PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], x_mirror=0, y_mirror=0)) # ######################### Text Fields ############################### addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge, courtyard={'top': cy1, 'bottom': -cy1}, fp_name=fp_name, text_y_inside_position='center') ##################### Output and 3d model ############################ kicad_mod.append(Model(filename=model_name)) output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name) if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity) os.makedirs(output_dir) filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name) file_handler = KicadFileHandler(kicad_mod) file_handler.writeFile(filename)
def __createFootprintVariant(self, device_params, header, dimensions, with_thermal_vias): fab_line_width = self.configuration.get('fab_line_width', 0.1) silk_line_width = self.configuration.get('silk_line_width', 0.12) lib_name = self.configuration['lib_name_format_string'].format( category=header['library_Suffix']) size_x = dimensions['body_size_x'].nominal size_y = dimensions['body_size_y'].nominal pincount = device_params['num_pins_x'] * 2 + device_params[ 'num_pins_y'] * 2 ipc_reference = 'ipc_spec_gw_large_pitch' if device_params[ 'pitch'] >= 0.625 else 'ipc_spec_gw_small_pitch' ipc_data_set = self.ipc_defintions[ipc_reference][ipc_density] ipc_round_base = self.ipc_defintions[ipc_reference]['round_base'] pitch = device_params['pitch'] pad_details = self.calcPadDetails(dimensions, ipc_data_set, ipc_round_base) suffix = device_params.get('suffix', '').format( pad_x=pad_details['left']['size'][0], pad_y=pad_details['left']['size'][1]) suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else "" model3d_path_prefix = self.configuration.get('3d_model_prefix', '${KISYS3DMOD}') name_format = self.configuration[ 'fp_name_format_string_no_trailing_zero'] EP_size = {'x': 0, 'y': 0} EP_mask_size = {'x': 0, 'y': 0} if dimensions['has_EP']: name_format = self.configuration[ 'fp_name_EP_format_string_no_trailing_zero'] EP_size = { 'x': dimensions['EP_size_x'].nominal, 'y': dimensions['EP_size_y'].nominal } if 'EP_mask_x' in dimensions: name_format = self.configuration[ 'fp_name_EP_custom_mask_format_string_no_trailing_zero'] EP_mask_size = { 'x': dimensions['EP_mask_x'].nominal, 'y': dimensions['EP_mask_y'].nominal } EP_size = Vector2D(EP_size) if 'custom_name_format' in device_params: name_format = device_params['custom_name_format'] fp_name = name_format.format( man=device_params.get('manufacturer', ''), mpn=device_params.get('part_number', ''), pkg=header['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], ep_size_x=EP_size['x'], ep_size_y=EP_size['y'], mask_size_x=EP_mask_size['x'], mask_size_y=EP_mask_size['y'], suffix=suffix, suffix2="", vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else '').replace('__', '_').lstrip('_') fp_name_2 = name_format.format( man=device_params.get('manufacturer', ''), mpn=device_params.get('part_number', ''), pkg=header['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], ep_size_x=EP_size['x'], ep_size_y=EP_size['y'], mask_size_x=EP_mask_size['x'], mask_size_y=EP_mask_size['y'], suffix=suffix_3d, suffix2="", vias='').replace('__', '_').lstrip('_') model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\ .format( model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=fp_name_2) #print(fp_name) #print(pad_details) kicad_mod = Footprint(fp_name) # init kicad footprint kicad_mod.setDescription( "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\ .format( manufacturer = device_params.get('manufacturer',''), package = header['device_type'], mpn = device_params.get('part_number',''), pincount = pincount, datasheet = device_params['size_source'], scriptname = os.path.basename(__file__).replace(" ", " ") ).lstrip()) kicad_mod.setTags(self.configuration['keyword_fp_string']\ .format( man=device_params.get('manufacturer',''), package=header['device_type'], category=header['library_Suffix'] ).lstrip()) kicad_mod.setAttribute('smd') pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get( 'round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration[ 'round_rect_max_radius'] EP_round_radius = 0 if dimensions['has_EP']: EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None if with_thermal_vias: thermals = device_params['thermal_vias'] paste_coverage = thermals.get( 'EP_paste_coverage', device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE)) EP = ExposedPad( number=pincount + 1, size=EP_size, mask_size=EP_mask_size, paste_layout=thermals.get('EP_num_paste_pads'), paste_coverage=paste_coverage, via_layout=thermals.get('count', 0), paste_between_vias=thermals.get('paste_between_vias'), paste_rings_outside=thermals.get('paste_rings_outside'), via_drill=thermals.get('drill', 0.3), via_grid=thermals.get('grid'), paste_avoid_via=thermals.get('paste_avoid_via', True), via_paste_clarance=thermals.get( 'paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE), min_annular_ring=thermals.get('min_annular_ring', DEFAULT_MIN_ANNULAR_RING), bottom_pad_min_size=thermals.get('bottom_min_size', 0), **pad_shape_details) else: EP = ExposedPad( number=pincount + 1, size=EP_size, mask_size=EP_mask_size, paste_layout=device_params.get('EP_num_paste_pads', 1), paste_coverage=device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE), **pad_shape_details) kicad_mod.append(EP) EP_round_radius = EP.getRoundRadius() pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) body_edge = { 'left': -dimensions['body_size_x'].nominal / 2, 'right': dimensions['body_size_x'].nominal / 2, 'top': -dimensions['body_size_y'].nominal / 2, 'bottom': dimensions['body_size_y'].nominal / 2 } bounding_box = { 'left': pad_details['left']['center'][0] - pad_details['left']['size'][0] / 2, 'right': pad_details['right']['center'][0] + pad_details['right']['size'][0] / 2, 'top': pad_details['top']['center'][1] - pad_details['top']['size'][1] / 2, 'bottom': pad_details['bottom']['center'][1] + pad_details['bottom']['size'][1] / 2 } if device_params['num_pins_x'] == 0: bounding_box['top'] = body_edge['top'] bounding_box['bottom'] = body_edge['bottom'] if EP_size['y'] > dimensions['body_size_y'].nominal: bounding_box['top'] = -EP_size['y'] / 2 bounding_box['bottom'] = EP_size['y'] / 2 if device_params['num_pins_y'] == 0: bounding_box['left'] = body_edge['left'] bounding_box['right'] = body_edge['right'] if EP_size['x'] > dimensions['body_size_x'].nominal: bounding_box['left'] = -EP_size['x'] / 2 bounding_box['right'] = EP_size['x'] / 2 pad_width = pad_details['top']['size'][0] # ############################ SilkS ################################## silk_pad_offset = configuration[ 'silk_pad_clearance'] + configuration['silk_line_width'] / 2 silk_offset = configuration['silk_fab_offset'] right_pads_silk_bottom = (device_params['num_pins_y']-1)*device_params['pitch']/2\ +pad_details['right']['size'][1]/2+silk_pad_offset silk_bottom = body_edge['bottom'] + silk_offset if EP_size['y'] / 2 <= body_edge[ 'bottom'] and right_pads_silk_bottom >= silk_bottom: silk_bottom = max(silk_bottom, EP_size['y'] / 2 + silk_pad_offset) silk_bottom = max(silk_bottom, right_pads_silk_bottom) silk_bottom = min(body_edge['bottom'] + silk_pad_offset, silk_bottom) bottom_pads_silk_right = (device_params['num_pins_x']-1)*device_params['pitch']/2\ +pad_details['bottom']['size'][0]/2+silk_pad_offset silk_right = body_edge['right'] + silk_offset if EP_size['x'] / 2 <= body_edge[ 'right'] and bottom_pads_silk_right >= silk_right: silk_right = max(silk_right, EP_size['x'] / 2 + silk_pad_offset) silk_right = max(silk_right, bottom_pads_silk_right) silk_right = min(body_edge['right'] + silk_pad_offset, silk_right) min_lenght = configuration.get('silk_line_lenght_min', 0) silk_corner_bottom_right = Vector2D(silk_right, silk_bottom) silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine( pad_size=EP_size, pad_position=[0, 0], pad_radius=EP_round_radius, fixed_point=silk_corner_bottom_right, moving_point=Vector2D(0, silk_bottom), silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_bottom_inside is not None and device_params[ 'num_pins_x'] > 0: silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['bottom']['size'], pad_position=[ pad_details['bottom']['center'][0] + (device_params['num_pins_x'] - 1) / 2 * pitch, pad_details['bottom']['center'][1] ], pad_radius=pad_radius, fixed_point=silk_corner_bottom_right, moving_point=silk_point_bottom_inside, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) silk_point_right_inside = nearestSilkPointOnOrthogonalLine( pad_size=EP_size, pad_position=[0, 0], pad_radius=EP_round_radius, fixed_point=silk_corner_bottom_right, moving_point=Vector2D(silk_right, 0), silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_right_inside is not None and device_params[ 'num_pins_y'] > 0: silk_point_right_inside = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['right']['size'], pad_position=[ pad_details['right']['center'][0], pad_details['right']['center'][1] + (device_params['num_pins_y'] - 1) / 2 * pitch ], pad_radius=pad_radius, fixed_point=silk_corner_bottom_right, moving_point=silk_point_right_inside, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) if silk_point_bottom_inside is None and silk_point_right_inside is not None: silk_corner_bottom_right['y'] = body_edge['bottom'] silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['bottom']['size'], pad_position=[ pad_details['bottom']['center'][0] + (device_params['num_pins_x'] - 1) / 2 * pitch, pad_details['bottom']['center'][1] ], pad_radius=pad_radius, fixed_point=silk_point_right_inside, moving_point=silk_corner_bottom_right, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) elif silk_point_right_inside is None and silk_point_bottom_inside is not None: silk_corner_bottom_right['x'] = body_edge['right'] silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine( pad_size=pad_details['right']['size'], pad_position=[ pad_details['right']['center'][0], pad_details['right']['center'][1] + (device_params['num_pins_y'] - 1) / 2 * pitch ], pad_radius=pad_radius, fixed_point=silk_point_bottom_inside, moving_point=silk_corner_bottom_right, silk_pad_offset=silk_pad_offset, min_lenght=min_lenght) kicad_mod.append( Line(start=silk_point_bottom_inside, end=silk_point_bottom_inside, width=configuration['silk_line_width'], layer="B.Fab")) poly_bottom_right = [] if silk_point_bottom_inside is not None: poly_bottom_right.append(silk_point_bottom_inside) poly_bottom_right.append(silk_corner_bottom_right) if silk_point_right_inside is not None: poly_bottom_right.append(silk_point_right_inside) if len(poly_bottom_right) > 1 and silk_corner_bottom_right is not None: kicad_mod.append( PolygoneLine(polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append( PolygoneLine(polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) kicad_mod.append( PolygoneLine(polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) if device_params['num_pins_y'] > 0: if silk_corner_bottom_right[ 'y'] - min_lenght >= right_pads_silk_bottom: poly_bottom_right[-1]['y'] = right_pads_silk_bottom kicad_mod.append( PolygoneLine(polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0, x_mirror=0)) kicad_mod.append( Line(start={ 'x': -silk_corner_bottom_right['x'], 'y': -right_pads_silk_bottom }, end={ 'x': bounding_box['left'], 'y': -right_pads_silk_bottom }, width=configuration['silk_line_width'], layer="F.SilkS")) elif silk_corner_bottom_right[ 'y'] >= right_pads_silk_bottom and silk_point_bottom_inside is not None: kicad_mod.append( Line(start=-silk_point_bottom_inside, end={ 'x': bounding_box['left'], 'y': -silk_point_bottom_inside['y'] }, width=configuration['silk_line_width'], layer="F.SilkS")) else: if silk_corner_bottom_right[ 'x'] - min_lenght >= bottom_pads_silk_right: poly_bottom_right[0]['x'] = bottom_pads_silk_right kicad_mod.append( PolygoneLine(polygone=poly_bottom_right, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0, x_mirror=0)) kicad_mod.append( Line(start={ 'x': -bottom_pads_silk_right, 'y': -silk_corner_bottom_right['y'] }, end={ 'x': -bottom_pads_silk_right, 'y': bounding_box['top'] }, width=configuration['silk_line_width'], layer="F.SilkS")) elif silk_corner_bottom_right[ 'x'] >= bottom_pads_silk_right and silk_point_right_inside is not None: kicad_mod.append( Line(start=-silk_point_right_inside, end={ 'x': -silk_point_right_inside['x'], 'y': bounding_box['top'] }, width=configuration['silk_line_width'], layer="F.SilkS")) # # ######################## Fabrication Layer ########################### fab_bevel_size = min( configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative'] * min(size_x, size_y)) poly_fab = [ { 'x': body_edge['left'] + fab_bevel_size, 'y': body_edge['top'] }, { 'x': body_edge['right'], 'y': body_edge['top'] }, { 'x': body_edge['right'], 'y': body_edge['bottom'] }, { 'x': body_edge['left'], 'y': body_edge['bottom'] }, { 'x': body_edge['left'], 'y': body_edge['top'] + fab_bevel_size }, { 'x': body_edge['left'] + fab_bevel_size, 'y': body_edge['top'] }, ] kicad_mod.append( PolygoneLine(polygone=poly_fab, width=configuration['fab_line_width'], layer="F.Fab")) # # ############################ CrtYd ################################## off = ipc_data_set['courtyard'] grid = configuration['courtyard_grid'] if device_params['num_pins_y'] == 0 or device_params['num_pins_x'] == 0: cy1 = roundToBase(bounding_box['top'] - off, grid) kicad_mod.append( RectLine(start={ 'x': roundToBase(bounding_box['left'] - off, grid), 'y': cy1 }, end={ 'x': roundToBase(bounding_box['right'] + off, grid), 'y': roundToBase(bounding_box['bottom'] + off, grid) }, width=configuration['courtyard_line_width'], layer='F.CrtYd')) else: cy1 = roundToBase(bounding_box['top'] - off, grid) cy2 = roundToBase(body_edge['top'] - off, grid) cy3 = -roundToBase( device_params['pitch'] * (device_params['num_pins_y'] - 1) / 2.0 + pad_width / 2.0 + off, grid) cx1 = -roundToBase( device_params['pitch'] * (device_params['num_pins_x'] - 1) / 2.0 + pad_width / 2.0 + off, grid) cx2 = roundToBase(body_edge['left'] - off, grid) cx3 = roundToBase(bounding_box['left'] - off, grid) crty_poly_tl = [{ 'x': 0, 'y': cy1 }, { 'x': cx1, 'y': cy1 }, { 'x': cx1, 'y': cy2 }, { 'x': cx2, 'y': cy2 }, { 'x': cx2, 'y': cy3 }, { 'x': cx3, 'y': cy3 }, { 'x': cx3, 'y': 0 }] kicad_mod.append( PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'])) kicad_mod.append( PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], x_mirror=0)) kicad_mod.append( PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], y_mirror=0)) kicad_mod.append( PolygoneLine(polygone=crty_poly_tl, layer='F.CrtYd', width=configuration['courtyard_line_width'], x_mirror=0, y_mirror=0)) # ######################### Text Fields ############################### addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge, courtyard={ 'top': cy1, 'bottom': -cy1 }, fp_name=fp_name, text_y_inside_position='center') ##################### Output and 3d model ############################ kicad_mod.append(Model(filename=model_name)) output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name) if not os.path.isdir( output_dir ): #returns false if path does not yet exist!! (Does not check path validity) os.makedirs(output_dir) filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name) file_handler = KicadFileHandler(kicad_mod) file_handler.writeFile(filename)
def __createFootprintVariant(self, device_params, device_dimensions, with_thermal_vias): fab_line_width = self.configuration.get('fab_line_width', 0.1) silk_line_width = self.configuration.get('silk_line_width', 0.12) lib_name = device_params.get('library', default_library) pincount = device_params['num_pins_x']*2 + device_params['num_pins_y']*2 default_ipc_config = 'qfn_pull_back' if 'lead_to_edge' in device_params else 'qfn' if device_params.get('ipc_class', default_ipc_config) == 'qfn_pull_back': ipc_reference = 'ipc_spec_flat_no_lead_pull_back' else: ipc_reference = 'ipc_spec_flat_no_lead' used_density = device_params.get('ipc_density', ipc_density) ipc_data_set = self.ipc_defintions[ipc_reference][used_density] ipc_round_base = self.ipc_defintions[ipc_reference]['round_base'] layout = '' if device_dimensions['has_EP']: name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero'] if 'EP_size_x_overwrite' in device_params: EP_size = { 'x':device_params['EP_size_x_overwrite'], 'y':device_params['EP_size_y_overwrite'] } else: EP_size = { 'x':device_dimensions['EP_size_x'].nominal, 'y':device_dimensions['EP_size_y'].nominal } EP_center = { 'x':device_dimensions['EP_center_x'].nominal, 'y':device_dimensions['EP_center_y'].nominal } else: name_format = self.configuration['fp_name_format_string_no_trailing_zero'] if device_params.get('use_name_format', 'QFN') == 'LGA': name_format = self.configuration['fp_name_lga_format_string_no_trailing_zero'] if device_params['num_pins_x'] > 0 and device_params['num_pins_y'] > 0: layout = self.configuration['lga_layout_border'].format( nx=device_params['num_pins_x'], ny=device_params['num_pins_y']) EP_size = {'x':0, 'y':0} if 'custom_name_format' in device_params: name_format = device_params['custom_name_format'] pad_details = self.calcPadDetails(device_dimensions, EP_size, ipc_data_set, ipc_round_base) pad_suffix = '_Pad{pad_x:.2f}x{pad_y:.2f}mm'.format(pad_x=pad_details['left']['size'][0], pad_y=pad_details['left']['size'][1]) pad_suffix = '' if device_params.get('include_pad_size', 'none') not in ('fp_name_only', 'both') else pad_suffix pad_suffix_3d = '' if device_params.get('include_pad_size', 'none') not in ('both') else pad_suffix suffix = device_params.get('suffix', '') suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else "" model3d_path_prefix = self.configuration.get('3d_model_prefix','${KISYS3DMOD}') size_x = device_dimensions['body_size_x'].nominal size_y = device_dimensions['body_size_y'].nominal fp_name = name_format.format( man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=device_params['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], layout=layout, ep_size_x = EP_size['x'], ep_size_y = EP_size['y'], suffix=pad_suffix, suffix2=suffix, vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else '' ).replace('__','_').lstrip('_') fp_name_2 = name_format.format( man=device_params.get('manufacturer',''), mpn=device_params.get('part_number',''), pkg=device_params['device_type'], pincount=pincount, size_y=size_y, size_x=size_x, pitch=device_params['pitch'], layout=layout, ep_size_x = EP_size['x'], ep_size_y = EP_size['y'], suffix=pad_suffix_3d, suffix2=suffix_3d, vias='' ).replace('__','_').lstrip('_') if 'fp_name_prefix' in device_params: prefix = device_params['fp_name_prefix'] if not prefix.endswith('_'): prefix += '_' fp_name = prefix + fp_name fp_name_2 = prefix + fp_name_2 model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\ .format( model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=fp_name_2) #print(fp_name) #print(pad_details) kicad_mod = Footprint(fp_name) # init kicad footprint kicad_mod.setDescription( "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\ .format( manufacturer = device_params.get('manufacturer',''), package = device_params['device_type'], mpn = device_params.get('part_number',''), pincount = pincount, datasheet = device_params['size_source'], scriptname = os.path.basename(__file__).replace(" ", " ") ).lstrip()) kicad_mod.setTags(self.configuration['keyword_fp_string']\ .format( man=device_params.get('manufacturer',''), package=device_params['device_type'], category=category ).lstrip()) kicad_mod.setAttribute('smd') pad_shape_details = {} pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0) if 'round_rect_max_radius' in configuration: pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius'] if device_dimensions['has_EP']: if with_thermal_vias: thermals = device_params['thermal_vias'] paste_coverage = thermals.get('EP_paste_coverage', device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE)) kicad_mod.append(ExposedPad( number=pincount+1, size=EP_size, at=EP_center, paste_layout=thermals.get('EP_num_paste_pads', device_params.get('EP_num_paste_pads', 1)), paste_coverage=paste_coverage, via_layout=thermals.get('count', 0), paste_between_vias=thermals.get('paste_between_vias'), paste_rings_outside=thermals.get('paste_rings_outside'), via_drill=thermals.get('drill', 0.3), via_grid=thermals.get('grid'), paste_avoid_via=thermals.get('paste_avoid_via', True), via_paste_clarance=thermals.get('paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE), min_annular_ring=thermals.get('min_annular_ring', DEFAULT_MIN_ANNULAR_RING), bottom_pad_min_size=thermals.get('bottom_min_size', 0), kicad4_compatible=args.kicad4_compatible, **pad_shape_details )) else: kicad_mod.append(ExposedPad( number=pincount+1, size=EP_size, at=EP_center, paste_layout=device_params.get('EP_num_paste_pads', 1), paste_coverage=device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE), kicad4_compatible=args.kicad4_compatible, **pad_shape_details )) add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params) body_edge = { 'left': -size_x/2, 'right': size_x/2, 'top': -size_y/2, 'bottom': size_y/2 } bounding_box = body_edge.copy() if device_params['num_pins_x'] == 0 and EP_size['y'] > size_y: bounding_box['top'] = -EP_size['y']/2 bounding_box['bottom'] = EP_size['y']/2 if device_params['num_pins_y'] == 0 and EP_size['x'] > size_x: bounding_box['left'] = -EP_size['x']/2 bounding_box['right'] = EP_size['x']/2 if device_params['num_pins_y'] > 0: bounding_box['left'] = pad_details['left']['center'][0] - pad_details['left']['size'][0]/2 bounding_box['right'] = pad_details['right']['center'][0] + pad_details['right']['size'][0]/2 if device_params['num_pins_x'] > 0: bounding_box['top'] = pad_details['top']['center'][1] - pad_details['top']['size'][1]/2 bounding_box['bottom'] = pad_details['bottom']['center'][1] + pad_details['bottom']['size'][1]/2 pad_width = pad_details['top']['size'][0] # ############################ SilkS ################################## silk_pad_offset = configuration['silk_pad_clearance'] + configuration['silk_line_width']/2 silk_offset = configuration['silk_fab_offset'] if device_params['num_pins_x'] == 0: kicad_mod.append(Line( start={'x':0, 'y':body_edge['top']-silk_offset}, end={'x':body_edge['right'], 'y':body_edge['top']-silk_offset}, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append(Line( start={'x':body_edge['left'], 'y':body_edge['bottom']+silk_offset}, end={'x':body_edge['right'], 'y':body_edge['bottom']+silk_offset}, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) elif device_params['num_pins_y'] == 0: kicad_mod.append(Line( start={'y':0, 'x':body_edge['left']-silk_offset}, end={'y':body_edge['bottom'], 'x':body_edge['left']-silk_offset}, width=configuration['silk_line_width'], layer="F.SilkS")) kicad_mod.append(Line( start={'y':body_edge['top'], 'x':body_edge['right']+silk_offset}, end={'y':body_edge['bottom'], 'x':body_edge['right']+silk_offset}, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) else: sx1 = -(device_params['pitch']*(device_params['num_pins_x']-1)/2.0 + pad_width/2.0 + silk_pad_offset) sy1 = -(device_params['pitch']*(device_params['num_pins_y']-1)/2.0 + pad_width/2.0 + silk_pad_offset) poly_silk = [ {'x': sx1, 'y': body_edge['top']-silk_offset}, {'x': body_edge['left']-silk_offset, 'y': body_edge['top']-silk_offset}, {'x': body_edge['left']-silk_offset, 'y': sy1} ] if sx1 - SILK_MIN_LEN < body_edge['left']-silk_offset: poly_silk = poly_silk[1:] if sy1 - SILK_MIN_LEN < body_edge['top']-silk_offset: poly_silk = poly_silk[:-1] if len(poly_silk) > 1: kicad_mod.append(PolygoneLine( polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0)) kicad_mod.append(PolygoneLine( polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", y_mirror=0)) kicad_mod.append(PolygoneLine( polygone=poly_silk, width=configuration['silk_line_width'], layer="F.SilkS", x_mirror=0, y_mirror=0)) if len(poly_silk) > 2: kicad_mod.append(Line( start={'x': sx1, 'y': body_edge['top']-silk_offset}, end={'x': body_edge['left']-silk_offset, 'y': body_edge['top']-silk_offset}, width=configuration['silk_line_width'], layer="F.SilkS")) # # ######################## Fabrication Layer ########################### fab_bevel_size = min(configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative']*min(size_x, size_y)) poly_fab = [ {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']}, {'x': body_edge['right'], 'y': body_edge['top']}, {'x': body_edge['right'], 'y': body_edge['bottom']}, {'x': body_edge['left'], 'y': body_edge['bottom']}, {'x': body_edge['left'], 'y': body_edge['top']+fab_bevel_size}, {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']}, ] kicad_mod.append(PolygoneLine( polygone=poly_fab, width=configuration['fab_line_width'], layer="F.Fab")) # # ############################ CrtYd ################################## off = ipc_data_set['courtyard'] grid = configuration['courtyard_grid'] cy1=roundToBase(bounding_box['top']-off, grid) kicad_mod.append(RectLine( start={ 'x':roundToBase(bounding_box['left']-off, grid), 'y':cy1 }, end={ 'x':roundToBase(bounding_box['right']+off, grid), 'y':roundToBase(bounding_box['bottom']+off, grid) }, width=configuration['courtyard_line_width'], layer='F.CrtYd')) # ######################### Text Fields ############################### addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge, courtyard={'top': cy1, 'bottom': -cy1}, fp_name=fp_name, text_y_inside_position='center') ##################### Output and 3d model ############################ kicad_mod.append(Model(filename=model_name)) output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name) if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity) os.makedirs(output_dir) filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name) file_handler = KicadFileHandler(kicad_mod) file_handler.writeFile(filename)