diff options
Diffstat (limited to 'src/electronics/simulation/bjt.cpp')
-rw-r--r-- | src/electronics/simulation/bjt.cpp | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/src/electronics/simulation/bjt.cpp b/src/electronics/simulation/bjt.cpp new file mode 100644 index 0000000..8daab40 --- /dev/null +++ b/src/electronics/simulation/bjt.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "bjt.h" +#include "diode.h" +#include "elementset.h" +#include "matrix.h" + +#include <cmath> +using namespace std; + + +//BEGIN class BJTSettings +BJTSettings::BJTSettings() +{ + I_S = 1e-16; + N_F = 1.0; + N_R = 1.0; + B_F = 100.0; + B_R = 1.0; +} +//END class BJTSettings + + + +//BEGIN class BJTState +BJTState::BJTState() +{ + reset(); +} + + +void BJTState::reset() +{ + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + A[i][j] = 0.0; + + I[i] = 0.0; + } +} + + +BJTState BJTState::operator-( const BJTState & s ) const +{ + BJTState newState( *this ); + + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + newState.A[i][j] -= s.A[i][j]; + + newState.I[i] -= s.I[i]; + } + + return newState; +} +//END class BJTState + + + +//BEGIN class BJT +BJT::BJT( const bool isNPN ) + : NonLinear() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_pol = isNPN ? 1 : -1; + m_numCNodes = 3; +} + + +BJT::~BJT() +{ +} + + +void BJT::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[2]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } +} + + +void BJT::add_initial_dc() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_os.reset(); + update_dc(); +} + + +void BJT::updateCurrents() +{ + if (!b_status) + return; + + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + m_cnodeI[1] = I_BC - I_T; + m_cnodeI[2] = I_BE + I_T; + m_cnodeI[0] = -(m_cnodeI[1] + m_cnodeI[2]); +} + + +void BJT::update_dc() +{ + if (!b_status) + return; + + calc_eq(); + + BJTState diff = m_ns - m_os; + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0 ; j < 3; ++j ) + A_g( i, j ) += diff.A[i][j]; + + b_i( i ) += diff.I[i]; + } + + m_os = m_ns; +} + + +void BJT::calc_eq() +{ + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + + // adjust voltage to help convergence + double V_BEcrit = diodeCriticalVoltage( I_S, N_F * V_T ); + double V_BCcrit = diodeCriticalVoltage( I_S, N_R * V_T ); + V_BE_prev = V_BE = diodeVoltage( V_BE, V_BE_prev, V_T * N_F, V_BEcrit ); + V_BC_prev = V_BC = diodeVoltage( V_BC, V_BC_prev, V_T * N_R, V_BCcrit ); + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + double I_eq_B = I_BE - V_BE * g_BE; + double I_eq_C = I_BC - V_BC * g_BC; + double I_eq_E = I_T - V_BE * g_IF + V_BC * g_IR; + + m_ns.A[0][0] = g_BC + g_BE; + m_ns.A[0][1] = -g_BC; + m_ns.A[0][2] = -g_BE; + + m_ns.A[1][0] = -g_BC + (g_IF - g_IR); + m_ns.A[1][1] = g_IR + g_BC; + m_ns.A[1][2] = -g_IF; + + m_ns.A[2][0] = -g_BE - (g_IF - g_IR); + m_ns.A[2][1] = -g_IR; + m_ns.A[2][2] = g_BE + g_IF; + + m_ns.I[0] = (-I_eq_B - I_eq_C) * m_pol; + m_ns.I[1] = (+I_eq_C - I_eq_E) * m_pol; + m_ns.I[2] = (+I_eq_B + I_eq_E) * m_pol; +} + + +void BJT::calcIg( double V_BE, double V_BC, double * I_BE, double * I_BC, double * I_T, double * g_BE, double * g_BC, double * g_IF, double * g_IR ) +{ + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + double B_F = m_bjtSettings.B_F; + double B_R = m_bjtSettings.B_R; + + // base-emitter diodes + double g_tiny = (V_BE < (-10 * V_T * N_F)) ? I_S : 0; + + double I_F; + diodeJunction( V_BE, I_S, V_T * N_F, & I_F, g_IF ); + + double I_BEI = I_F / B_F; + double g_BEI = *g_IF / B_F; + double I_BEN = g_tiny * V_BE; + double g_BEN = g_tiny; + *I_BE = I_BEI + I_BEN; + *g_BE = g_BEI + g_BEN; + + // base-collector diodes + g_tiny = (V_BC < (-10 * V_T * N_R)) ? I_S : 0; + + double I_R; + diodeJunction( V_BC, I_S, V_T * N_R, & I_R, g_IR ); + + double I_BCI = I_R / B_R; + double g_BCI = *g_IR / B_R; + double I_BCN = g_tiny * V_BC; + double g_BCN = g_tiny; + *I_BC = I_BCI + I_BCN; + *g_BC = g_BCI + g_BCN; + + *I_T = I_F - I_R; +} + + +void BJT::setBJTSettings( const BJTSettings & settings ) +{ + m_bjtSettings = settings; + + if (p_eSet) + p_eSet->setCacheInvalidated(); +} +//END class BJT + + |