/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 1992-2019 Kicad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /* All calculations are based on this [1] online calculator: * * References: * * [1]: The CircuitCalculator.com Blog - PCB Via Calculator * http://circuitcalculator.com/wordpress/2006/03/12/pcb-via-calculator/ * * [2]: Constructing Your Power Supply - Layout Considerations * https://www.ti.com/seclit/ml/slup230/slup230.pdf * * [3]: Current Carrying Capacity of Vias - Some Conceptual Observations * https://www.ultracad.com/articles/viacurrents.pdf * * [4]: IPC-2221A - Generic Standard on Printed Board Design * http://www.sphere.bc.ca/class/downloads/ipc_2221a-pcb%20standards.pdf * * [5]: Copper - online catalogue source - Goodfellow * http://www.goodfellow.com/E/Copper.html * * [6]: Thermal Conductivity of Metals, Metallic Elements and Alloys * https://www.engineeringtoolbox.com/thermal-conductivity-metals-d_858.html * * [7]: Johnson & Graham, High Speed Digital Design: A Handbook of Black Magic */ #include #include #include #include #include #include #include #include extern double DoubleFromString( const wxString& TextValue ); /** * Shows a list of current relative dielectric constant(Er) * and set the selected value in main dialog frame */ void PCB_CALCULATOR_FRAME::OnViaEpsilonR_Button( wxCommandEvent& event ) { wxArrayString list = StandardRelativeDielectricConstantList(); wxString value = wxGetSingleChoice( wxEmptyString, _("Relative Dielectric Constants"), list).BeforeFirst( ' ' ); if( ! value.IsEmpty() ) m_textCtrlPlatingPermittivity->SetValue( value ); } /** * Shows a list of current Specific resistance list (rho) * and set the selected value in main dialog frame */ void PCB_CALCULATOR_FRAME::OnViaRho_Button( wxCommandEvent& event ) { wxArrayString list = StandardResistivityList(); wxString value = wxGetSingleChoice( wxEmptyString, _("Electrical Resistivity in Ohm*m"), list).BeforeFirst( ' ' ); if( ! value.IsEmpty() ) m_textCtrlPlatingResistivity->SetValue( value ); } void PCB_CALCULATOR_FRAME::onUpdateViaCalcErrorText( wxUpdateUIEvent& event ) { // Update the Error message if a via has a external diameter // bigger than the clearance area diameter // (therefore the via is inside a copper zone and some parameters cannot be calculated) double clearanceDia = std::abs( DoubleFromString( m_textCtrlClearanceDia->GetValue() ) ); clearanceDia *= m_choiceClearanceDia->GetUnitScale(); double padDia = std::abs( DoubleFromString( m_textCtrlViaPadDia->GetValue() ) ); padDia *= m_choiceViaPadDia->GetUnitScale(); m_staticTextWarning->Show( clearanceDia <= padDia ); } void PCB_CALCULATOR_FRAME::OnViaResetButtonClick( wxCommandEvent& event ) { #define DEFAULT_UNIT_SEL_MM 0 m_textCtrlHoleDia->SetValue( wxT( "0.4" ) ); m_choiceHoleDia->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlPlatingThickness->SetValue( wxT( "0.035" ) ); m_choicePlatingThickness->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlViaLength->SetValue( wxT( "1.6" ) ); m_choiceViaLength->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlViaPadDia->SetValue( wxT( "0.6" ) ); m_choiceViaPadDia->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlClearanceDia->SetValue( wxT( "1.0" ) ); m_choiceClearanceDia->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlImpedance->SetValue( wxT( "50" ) ); m_choiceImpedance->SetSelection( DEFAULT_UNIT_SEL_MM ); m_textCtrlAppliedCurrent->SetValue( wxT( "1" ) ); m_textCtrlPlatingResistivity->SetValue( wxT( "1.72e-8" ) ); m_textCtrlPlatingPermittivity->SetValue( wxT( "4.5" ) ); m_textCtrlTemperatureDiff->SetValue( wxT( "10" ) ); m_textCtrlRiseTime->SetValue( wxT( "1" ) ); } void PCB_CALCULATOR_FRAME::VS_Init() { auto cfg = static_cast( Kiface().KifaceSettings() ); m_textCtrlHoleDia->SetValue( cfg->m_ViaSize.hole_diameter ); m_choiceHoleDia->SetSelection( cfg->m_ViaSize.hole_diameter_units ); m_textCtrlPlatingThickness->SetValue( cfg->m_ViaSize.thickness ); m_choicePlatingThickness->SetSelection( cfg->m_ViaSize.thickness_units ); m_textCtrlViaLength->SetValue( cfg->m_ViaSize.length ); m_choiceViaLength->SetSelection( cfg->m_ViaSize.length_units ); m_textCtrlViaPadDia->SetValue( cfg->m_ViaSize.pad_diameter ); m_choiceViaPadDia->SetSelection( cfg->m_ViaSize.pad_diameter_units ); m_textCtrlClearanceDia->SetValue( cfg->m_ViaSize.clearance_diameter ); m_choiceClearanceDia->SetSelection( cfg->m_ViaSize.clearance_diameter_units ); m_textCtrlImpedance->SetValue( cfg->m_ViaSize.characteristic_impedance ); m_choiceImpedance->SetSelection( cfg->m_ViaSize.characteristic_impedance_units ); m_textCtrlAppliedCurrent->SetValue( cfg->m_ViaSize.applied_current ); m_textCtrlPlatingResistivity->SetValue( cfg->m_ViaSize.plating_resistivity ); m_textCtrlPlatingPermittivity->SetValue( cfg->m_ViaSize.permittivity ); m_textCtrlTemperatureDiff->SetValue( cfg->m_ViaSize.temp_rise ); m_textCtrlRiseTime->SetValue( cfg->m_ViaSize.pulse_rise_time ); } void PCB_CALCULATOR_FRAME::VS_WriteConfig() { auto cfg = static_cast( Kiface().KifaceSettings() ); cfg->m_ViaSize.hole_diameter = m_textCtrlHoleDia->GetValue(); cfg->m_ViaSize.hole_diameter_units = m_choiceHoleDia->GetSelection(); cfg->m_ViaSize.thickness = m_textCtrlPlatingThickness->GetValue(); cfg->m_ViaSize.thickness_units = m_choicePlatingThickness->GetSelection(); cfg->m_ViaSize.length = m_textCtrlViaLength->GetValue(); cfg->m_ViaSize.length_units = m_choiceViaLength->GetSelection(); cfg->m_ViaSize.pad_diameter = m_textCtrlViaPadDia->GetValue(); cfg->m_ViaSize.pad_diameter_units = m_choiceViaPadDia->GetSelection(); cfg->m_ViaSize.clearance_diameter = m_textCtrlClearanceDia->GetValue(); cfg->m_ViaSize.clearance_diameter_units = m_choiceClearanceDia->GetSelection(); cfg->m_ViaSize.characteristic_impedance = m_textCtrlImpedance->GetValue(); cfg->m_ViaSize.characteristic_impedance_units = m_choiceImpedance->GetSelection(); cfg->m_ViaSize.applied_current = m_textCtrlAppliedCurrent->GetValue(); cfg->m_ViaSize.plating_resistivity = m_textCtrlPlatingResistivity->GetValue(); cfg->m_ViaSize.permittivity = m_textCtrlPlatingPermittivity->GetValue(); cfg->m_ViaSize.temp_rise = m_textCtrlTemperatureDiff->GetValue(); cfg->m_ViaSize.pulse_rise_time = m_textCtrlRiseTime->GetValue(); } void PCB_CALCULATOR_FRAME::OnViaCalculate( wxCommandEvent& event ) { // Load parameters double finishedHoleDia = std::abs( DoubleFromString( m_textCtrlHoleDia->GetValue() ) ); double platingThickness = std::abs( DoubleFromString( m_textCtrlPlatingThickness->GetValue() ) ); double viaLength = std::abs( DoubleFromString( m_textCtrlViaLength->GetValue() ) ); double padDia = std::abs( DoubleFromString( m_textCtrlViaPadDia->GetValue() ) ); double clearanceDia = std::abs( DoubleFromString( m_textCtrlClearanceDia->GetValue() ) ); double charImpedance = std::abs( DoubleFromString( m_textCtrlImpedance->GetValue() ) ); double appliedCurrent = std::abs( DoubleFromString( m_textCtrlAppliedCurrent->GetValue() ) ); double platingResistivity = std::abs( DoubleFromString( m_textCtrlPlatingResistivity->GetValue() ) ); double relativePermitivity = std::abs( DoubleFromString( m_textCtrlPlatingPermittivity->GetValue() ) ); double temperatureDiff = std::abs( DoubleFromString( m_textCtrlTemperatureDiff->GetValue() ) ); double pulseRiseTime = std::abs( DoubleFromString( m_textCtrlRiseTime->GetValue() ) ); // Normalize units finishedHoleDia *= m_choiceHoleDia->GetUnitScale(); platingThickness *= m_choicePlatingThickness->GetUnitScale(); viaLength *= m_choiceViaLength->GetUnitScale(); padDia *= m_choiceViaPadDia->GetUnitScale(); clearanceDia *= m_choiceClearanceDia->GetUnitScale(); charImpedance *= m_choiceImpedance->GetUnitScale(); // platingResistivity is ok: it is in Ohm*m in tables // Calculate cross-sectional area of the via's cylindrical structure [3] double area = M_PI * (finishedHoleDia + platingThickness) * platingThickness; // m^2 double viaResistance = platingResistivity * viaLength / area; // Ohms // Using thermal resistivity value 2.49e-3 meter-Kelvin/Watt, equivalent to // thermal conductivity of 401 Watt/(meter-Kelvin) [5][6] const double thermalResistivity = 2.49e-3; // m K/W double thermalResistance = thermalResistivity * viaLength / area; // deg C/W double voltageDrop = appliedCurrent * viaResistance; double powerLoss = appliedCurrent * voltageDrop; // Estimate current carrying capacity of the via // See comment #17 in [1] for a brief discussion on the formula // This formula from IPC-2221 [4] is also used in the Track Width calculator area /= pow( UNIT_MIL, 2 ); // m^2 to mil^2 const double k = 0.048; const double b = 0.44; const double c = 0.725; double estimatedAmpacity = k * pow( temperatureDiff, b ) * pow( area, c ); // Equation 7.6 in [7] double capacitance = 55.51 * relativePermitivity * viaLength * padDia; capacitance /= clearanceDia - padDia; // Equation 7.8 in [7] double timeDegradation = 2.2 * capacitance * charImpedance / 2; // Equation 7.9 in [7] double inductance = 200 * viaLength; inductance *= log( 4 * viaLength / finishedHoleDia ) + 1; // Equation 7.11 in [7] double reactance = M_PI * inductance / pulseRiseTime; // Update the display VSDisplayValues( viaResistance, voltageDrop, powerLoss, estimatedAmpacity, thermalResistance, capacitance, timeDegradation, inductance, reactance ); } void PCB_CALCULATOR_FRAME::VSDisplayValues( double aViaResistance, double aVoltageDrop, double aPowerLoss, double aEstimatedAmpacity, double aThermalResistance, double aCapacitance, double aTimeDegradation, double aInductance, double aReactance ) { wxString msg; msg.Printf( "%g", aViaResistance ); m_ViaResistance->SetLabel( msg ); msg.Printf( "%g", aVoltageDrop ); m_ViaVoltageDrop->SetLabel( msg ); msg.Printf( "%g", aPowerLoss ); m_ViaPowerLoss->SetLabel( msg ); msg.Printf( "%g", aEstimatedAmpacity ); m_ViaAmpacity->SetLabel( msg ); msg.Printf( "%g", aThermalResistance ); m_ViaThermalResistance->SetLabel( msg ); msg.Printf( "%g", aCapacitance ); m_ViaCapacitance->SetLabel( msg ); msg.Printf( "%g", aTimeDegradation ); m_RiseTimeOutput->SetLabel( msg ); msg.Printf( "%g", aInductance ); m_Inductance->SetLabel( msg ); msg.Printf( "%g", aReactance ); m_Reactance->SetLabel( msg ); }