Pcbnew: add pad fabrication property Press-Fit

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21457
This commit is contained in:
jean-pierre charras 2025-08-11 15:25:20 +02:00
parent ea8d0519bd
commit da92279436
17 changed files with 93 additions and 22 deletions

View File

@ -269,6 +269,11 @@ std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribu
attribute_string = "TA.AperFunction,ComponentDrill";
break;
case GBR_APERTURE_ATTRIB_PRESSFITDRILL:
// print info associated to a flashed component pad with pressfit option in drill files
attribute_string = "TA.AperFunction,ComponentDrill,PressFit";
break;
// print info associated to a component oblong pad hole in drill files
// Same as a round pad hole, but is a specific aperture in drill file and
// a G04 comment is added to the aperture function

View File

@ -263,6 +263,7 @@ pad_prop_castellated
pad_prop_testpoint
pad_prop_heatsink
pad_prop_mechanical
pad_prop_pressfit
padstack
padvia
prefer_zone_connections

View File

@ -137,6 +137,10 @@ public:
/// Aperture used for castellated pads in drill files.
GBR_APERTURE_ATTRIB_CASTELLATEDDRILL,
/// Aperture used for pressfit pads in drill files.
/// this is similar to GBR_APERTURE_ATTRIB_COMPONENTPAD with optional PressFit field
GBR_APERTURE_ATTRIB_PRESSFITDRILL,
GBR_APERTURE_ATTRIB_VIADRILL, ///< Aperture used for via holes in drill files.
GBR_APERTURE_ATTRIB_CMP_DRILL, ///< Aperture used for pad holes in drill files.

View File

@ -110,6 +110,8 @@ DIALOG_GENDRILL::DIALOG_GENDRILL( PCB_EDIT_FRAME* aPcbEditFrame, JOB_EXPORT_PCB_
bool DIALOG_GENDRILL::TransferDataToWindow()
{
m_messagesBox->Clear();
if( !m_job )
{
updatePrecisionOptions();
@ -292,6 +294,7 @@ void DIALOG_GENDRILL::genDrillAndMapFiles( bool aGenDrill, bool aGenMap, bool aG
updateConfig(); // set params and Save drill options
m_pcbEditFrame->ClearMsgPanel();
m_messagesBox->Clear();
WX_TEXT_CTRL_REPORTER reporter( m_messagesBox );
const PLOT_FORMAT filefmt[] = {
@ -395,6 +398,7 @@ void DIALOG_GENDRILL::onGenReportFile( wxCommandEvent& event )
if( dlg.ShowModal() == wxID_CANCEL )
return;
m_messagesBox->Clear();
bool success;
// Info is slightly different between Excellon and Gerber

View File

@ -805,6 +805,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
case PAD_PROP::HEATSINK: m_choiceFabProperty->SetSelection( 5 ); break;
case PAD_PROP::CASTELLATED: m_choiceFabProperty->SetSelection( 6 ); break;
case PAD_PROP::MECHANICAL: m_choiceFabProperty->SetSelection( 7 ); break;
case PAD_PROP::PRESSFIT: m_choiceFabProperty->SetSelection( 8 ); break;
}
// Ensure the pad property is compatible with the pad type
@ -1700,6 +1701,7 @@ PAD_PROP DIALOG_PAD_PROPERTIES::getSelectedProperty()
case 5: prop = PAD_PROP::HEATSINK; break;
case 6: prop = PAD_PROP::CASTELLATED; break;
case 7: prop = PAD_PROP::MECHANICAL; break;
case 8: prop = PAD_PROP::PRESSFIT; break;
}
return prop;

View File

@ -613,7 +613,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_middleBoxSizer->Add( 0, 2, 0, wxEXPAND, 5 );
wxString m_choiceFabPropertyChoices[] = { _("None"), _("BGA pad"), _("Fiducial, local to footprint"), _("Fiducial, global to board"), _("Test point pad"), _("Heatsink pad"), _("Castellated pad (through hole only)"), _("Mechanical") };
wxString m_choiceFabPropertyChoices[] = { _("None"), _("BGA pad"), _("Fiducial, local to footprint"), _("Fiducial, global to board"), _("Test point pad"), _("Heatsink pad"), _("Castellated pad (through hole only)"), _("Mechanical"), _("Press-fit (through hole only)") };
int m_choiceFabPropertyNChoices = sizeof( m_choiceFabPropertyChoices ) / sizeof( wxString );
m_choiceFabProperty = new wxChoice( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceFabPropertyNChoices, m_choiceFabPropertyChoices, 0 );
m_choiceFabProperty->SetSelection( 0 );

View File

@ -7477,7 +7477,7 @@
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;None&quot; &quot;BGA pad&quot; &quot;Fiducial, local to footprint&quot; &quot;Fiducial, global to board&quot; &quot;Test point pad&quot; &quot;Heatsink pad&quot; &quot;Castellated pad (through hole only)&quot; &quot;Mechanical&quot;</property>
<property name="choices">&quot;None&quot; &quot;BGA pad&quot; &quot;Fiducial, local to footprint&quot; &quot;Fiducial, global to board&quot; &quot;Test point pad&quot; &quot;Heatsink pad&quot; &quot;Castellated pad (through hole only)&quot; &quot;Mechanical&quot; &quot;Press-fit (through hole only)&quot;</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>

View File

@ -362,19 +362,29 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
diameter_in_inches( tool.m_Diameter ) );
msg = From_UTF8( line );
const char* extra_info;
// Add more info for holes with specific constraints (castelleted, pressfit)
// note: only PTH pads have this option
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED )
extra_info = ", castellated";
else if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT )
extra_info = ", press-fit";
else
extra_info = "";
// Now list how many holes and ovals are associated with each drill.
if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
snprintf( line, sizeof(line), "(1 hole)" );
snprintf( line, sizeof(line), "(1 hole%s)", extra_info );
else if( tool.m_TotalCount == 1 ) // && ( toolm_OvalCount == 1 )
snprintf( line, sizeof(line), "(1 slot)" );
snprintf( line, sizeof(line), "(1 slot%s)", extra_info );
else if( tool.m_OvalCount == 0 )
snprintf( line, sizeof(line), "(%d holes)", tool.m_TotalCount );
snprintf( line, sizeof(line), "(%d holes%s)", tool.m_TotalCount, extra_info );
else if( tool.m_OvalCount == 1 )
snprintf( line, sizeof(line), "(%d holes + 1 slot)", tool.m_TotalCount - 1 );
snprintf( line, sizeof(line), "(%d holes + 1 slot%s)", tool.m_TotalCount - 1, extra_info );
else // if ( toolm_OvalCount > 1 )
snprintf( line, sizeof(line), "(%d holes + %d slots)", tool.m_TotalCount - tool.m_OvalCount,
tool.m_OvalCount );
snprintf( line, sizeof(line), "(%d holes + %d slots%s)", tool.m_TotalCount - tool.m_OvalCount,
tool.m_OvalCount, extra_info );
msg += From_UTF8( line );
@ -550,15 +560,23 @@ unsigned GENDRILL_WRITER_BASE::printToolSummary( OUTPUTFORMATTER& out, bool aSum
// Now list how many holes and ovals are associated with each drill.
if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
out.Print( 0, "(1 hole)\n" );
out.Print( 0, "(1 hole" );
else if( tool.m_TotalCount == 1 )
out.Print( 0, "(1 hole) (with 1 slot)\n" );
out.Print( 0, "(1 hole) (with 1 slot" );
else if( tool.m_OvalCount == 0 )
out.Print( 0, "(%d holes)\n", tool.m_TotalCount );
out.Print( 0, "(%d holes)", tool.m_TotalCount );
else if( tool.m_OvalCount == 1 )
out.Print( 0, "(%d holes) (with 1 slot)\n", tool.m_TotalCount );
out.Print( 0, "(%d holes) (with 1 slot", tool.m_TotalCount );
else // tool.m_OvalCount > 1
out.Print( 0, "(%d holes) (with %d slots)\n", tool.m_TotalCount, tool.m_OvalCount );
out.Print( 0, "(%d holes) (with %d slots", tool.m_TotalCount, tool.m_OvalCount );
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED )
out.Print( 0, ", castellated" );
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT )
out.Print( 0, ", press-fit" );
out.Print( 0, ")\n");
totalHoleCount += tool.m_TotalCount;
}

View File

@ -176,9 +176,18 @@ void EXCELLON_WRITER::writeHoleAttribute( HOLE_ATTRIBUTE aAttribute )
break;
case HOLE_ATTRIBUTE::HOLE_PAD:
//case HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED:
fprintf( m_file, "; #@! TA.AperFunction,Plated,PTH,ComponentDrill\n" );
break;
case HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED:
fprintf( m_file, "; #@! TA.AperFunction,Plated,PTH,CastelletedDrill\n" );
break;
case HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT:
fprintf( m_file, "; #@! TA.AperFunction,Plated,PTH,ComponentDrill,PressFit\n" );
break;
case HOLE_ATTRIBUTE::HOLE_MECHANICAL:
fprintf( m_file, "; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill\n" );
break;

View File

@ -147,9 +147,20 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGe
continue;
new_hole.m_ItemParent = pad;
new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB::NPTH);
new_hole.m_HoleAttribute = new_hole.m_Hole_NotPlated ? HOLE_ATTRIBUTE::HOLE_MECHANICAL
: HOLE_ATTRIBUTE::HOLE_PAD;
new_hole.m_Hole_NotPlated = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
if( new_hole.m_Hole_NotPlated )
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_MECHANICAL;
else
{
if( pad->GetProperty() == PAD_PROP::CASTELLATED )
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED;
else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT;
else
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD;
}
new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
new_hole.m_Hole_Orient = pad->GetOrientation();
new_hole.m_Hole_Shape = 0; // hole shape: round

View File

@ -43,11 +43,13 @@ class BOARD_ITEM;
// in NC drill files
enum class HOLE_ATTRIBUTE
{
HOLE_UNKNOWN, // uninitialized type
HOLE_VIA_THROUGH, // a via hole (always plated) from top to bottom
HOLE_VIA_BURIED, // a via hole (always plated) not through hole
HOLE_PAD, // a plated or not plated pad hole
HOLE_MECHANICAL // a mechanical pad (provided, not used)
HOLE_UNKNOWN, // uninitialized type
HOLE_VIA_THROUGH, // a via hole (always plated) from top to bottom
HOLE_VIA_BURIED, // a via hole (always plated) not through hole
HOLE_PAD, // a plated or not plated pad hole
HOLE_PAD_CASTELLATED, // a plated castelleted pad hole
HOLE_PAD_PRESSFIT, // a plated press-fit pad hole
HOLE_MECHANICAL // a mechanical pad (provided, not used)
};
// Via Protection features according to IPC-4761.

View File

@ -390,6 +390,11 @@ int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
gbr_metadata.SetApertureAttrib(
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CASTELLATEDDRILL );
}
else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
{
gbr_metadata.SetApertureAttrib(
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_PRESSFITDRILL );
}
else
{
// Good practice of oblong pad holes (slots) is to use a specific aperture for

View File

@ -1400,6 +1400,7 @@ void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
case PAD_PROP::HEATSINK: props += _( "Heat sink" ); break;
case PAD_PROP::CASTELLATED: props += _( "Castellated" ); break;
case PAD_PROP::MECHANICAL: props += _( "Mechanical" ); break;
case PAD_PROP::PRESSFIT: props += _( "Press-fit" ); break;
}
// TODO(JE) How to show complex padstack info in the message panel
@ -2309,6 +2310,10 @@ void PAD::CheckPad( UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
if( GetProperty() == PAD_PROP::MECHANICAL && GetAttribute() != PAD_ATTRIB::PTH )
aErrorHandler( DRCE_PADSTACK, _( "('mechanical' property is for PTH pads)" ) );
if( GetProperty() == PAD_PROP::PRESSFIT
&& ( GetAttribute() != PAD_ATTRIB::PTH || !HasDrilledHole() ) )
aErrorHandler( DRCE_PADSTACK, _( "('press-fit' property is for PTH pads with round holes)" ) );
switch( GetAttribute() )
{
case PAD_ATTRIB::NPTH: // Not plated, but through hole, a hole is expected
@ -2745,7 +2750,8 @@ static struct PAD_DESC
.Map( PAD_PROP::TESTPOINT, _HKI( "Test point pad" ) )
.Map( PAD_PROP::HEATSINK, _HKI( "Heatsink pad" ) )
.Map( PAD_PROP::CASTELLATED, _HKI( "Castellated pad" ) )
.Map( PAD_PROP::MECHANICAL, _HKI( "Mechanical pad" ) );
.Map( PAD_PROP::MECHANICAL, _HKI( "Mechanical pad" ) )
.Map( PAD_PROP::PRESSFIT, _HKI( "Press-fit pad" ) );
ENUM_MAP<PAD_DRILL_SHAPE>::Instance()
.Map( PAD_DRILL_SHAPE::CIRCLE, _HKI( "Round" ) )

View File

@ -104,6 +104,7 @@ enum class PAD_PROP
HEATSINK, ///< a pad used as heat sink, usually in SMD footprints
CASTELLATED, ///< a pad with a castellated through hole
MECHANICAL, ///< a pad used for mechanical support
PRESSFIT ///< a PTH with a hole diameter with tight tolerances for press fit pin
};

View File

@ -1526,6 +1526,7 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
case PAD_PROP::MECHANICAL: property = "pad_prop_mechanical"; break;
case PAD_PROP::PRESSFIT: property = "pad_prop_pressfit"; break;
default:
THROW_IO_ERROR( wxString::Format( wxT( "unknown pad property: %d" ),

View File

@ -5506,6 +5506,7 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
case T_pad_prop_castellated: pad->SetProperty( PAD_PROP::CASTELLATED ); break;
case T_pad_prop_heatsink: pad->SetProperty( PAD_PROP::HEATSINK ); break;
case T_pad_prop_mechanical: pad->SetProperty( PAD_PROP::MECHANICAL ); break;
case T_pad_prop_pressfit: pad->SetProperty( PAD_PROP::PRESSFIT ); break;
case T_none: pad->SetProperty( PAD_PROP::NONE ); break;
case T_RIGHT: break;

View File

@ -258,6 +258,7 @@ void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, PCB_LAYER_ID aLayer, const COLO
metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CASTELLATEDPAD );
break;
case PAD_PROP::PRESSFIT: // used only in drill files
case PAD_PROP::NONE:
case PAD_PROP::MECHANICAL:
break;