diff options
Diffstat (limited to 'src/lib/netlist/devices/nld_ne555.cpp')
-rw-r--r-- | src/lib/netlist/devices/nld_ne555.cpp | 286 |
1 files changed, 125 insertions, 161 deletions
diff --git a/src/lib/netlist/devices/nld_ne555.cpp b/src/lib/netlist/devices/nld_ne555.cpp index dc6e3e5696a..a94d868373d 100644 --- a/src/lib/netlist/devices/nld_ne555.cpp +++ b/src/lib/netlist/devices/nld_ne555.cpp @@ -1,61 +1,17 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/*! - * \file nld_NE555.cpp - * - * \page NE555 NE555: PRECISION TIMERS - * - * The Swiss army knife for timing purposes - * - * \section ne555_1 Synopsis - * - * \snippet devsyn.dox.h NE555 synopsis - * \snippet devsyn.dox.h NE555_DIP synopsis - - * \section ne555_11 "C" Synopsis - * - * \snippet devsyn.dox.h NE555 csynopsis - * \snippet devsyn.dox.h NE555_DIP csynopsis - * - * For the \c NE555 use verbose pin assignments like \c name.TRIG or \c name.OUT. - * For the \c NE555_DIP use pin numbers like \c name.1. - * - * \section ne555_2 Connection Diagram - * - * <pre> - * +--------+ - * GND |1 ++ 8| VCC - * TRIG |2 7| DISCH - * OUT |3 6| THRES - * RESET |4 5| CONT - * +--------+ - * </pre> - * - * Naming conventions follow Texas Instruments datasheet - * - * \section ne555_3 Function Table - * - * Please refer to the datasheet. - * - * \section ne555_4 Limitations - * - * Internal resistor network currently fixed to 5k. - * - * \section ne555_5 Example - * \snippet ne555_astable.c ne555 example - */ - -#include "nld_ne555.h" -#include "netlist/analog/nlid_twoterm.h" -#include "netlist/solver/nld_solver.h" + + +#include "analog/nlid_twoterm.h" +#include "solver/nld_solver.h" + +#include "plib/pmath.h" #define R_OFF (1E20) -#define R_ON (25) // Datasheet states a maximum discharge of 200mA, R = 5V / 0.2 +#define R_ON (1) + +namespace netlist::devices { -namespace netlist -{ - namespace devices - { NETLIB_OBJECT(NE555) { NETLIB_CONSTRUCTOR(NE555) @@ -64,35 +20,122 @@ namespace netlist , m_R3(*this, "R3") , m_ROUT(*this, "ROUT") , m_RDIS(*this, "RDIS") - , m_RESET(*this, "RESET") // Pin 4 - , m_THRES(*this, "THRESH") // Pin 6 - , m_TRIG(*this, "TRIG") // Pin 2 + , m_RESET(*this, "RESET", NETLIB_DELEGATE(inputs)) // Pin 4 + , m_THRES(*this, "THRESH", NETLIB_DELEGATE(inputs)) // Pin 6 + , m_TRIG(*this, "TRIG", NETLIB_DELEGATE(inputs)) // Pin 2 , m_OUT(*this, "_OUT") // to Pin 3 via ROUT , m_last_out(*this, "m_last_out", false) , m_ff(*this, "m_ff", false) , m_last_reset(*this, "m_last_reset", false) + , m_overshoot(*this, "m_overshoot", 0.0) + , m_undershoot(*this, "m_undershoot", 0.0) + , m_overshoot_limit(0.0) { - register_subalias("GND", m_R3.m_N); // Pin 1 - register_subalias("CONT", m_R1.m_N); // Pin 5 - register_subalias("DISCH", m_RDIS.m_P); // Pin 7 - register_subalias("VCC", m_R1.m_P); // Pin 8 - register_subalias("OUT", m_ROUT.m_P); // Pin 3 - - connect(m_R1.m_N, m_R2.m_P); - connect(m_R2.m_N, m_R3.m_P); - connect(m_RDIS.m_N, m_R3.m_N); - connect(m_OUT, m_ROUT.m_N); + register_sub_alias("GND", "R3.2"); // Pin 1 + register_sub_alias("CONT", "R1.2"); // Pin 5 + register_sub_alias("DISCH", "RDIS.1"); // Pin 7 + register_sub_alias("VCC", "R1.1"); // Pin 8 + register_sub_alias("OUT", "ROUT.1"); // Pin 3 + + connect("R1.2", "R2.1"); + connect("R2.2", "R3.1"); + connect("RDIS.2", "R3.2"); + connect("_OUT", "ROUT.2"); } - NETLIB_UPDATEI(); - NETLIB_RESETI(); + NETLIB_RESETI() + { + /* FIXME make resistances a parameter, properly model other variants */ + m_R1().set_R(nlconst::magic(5000)); + m_R2().set_R(nlconst::magic(5000)); + m_R3().set_R(nlconst::magic(5000)); + m_ROUT().set_R(nlconst::magic(20)); + m_RDIS().set_R(nlconst::magic(R_OFF)); + + m_last_out = true; + // Check for astable setup, usually TRIG AND THRES connected. Enable + // overshoot compensation in this case. + if (m_TRIG.net() == m_THRES.net()) + m_overshoot_limit = nlconst::magic(0.5); + } private: - analog::NETLIB_SUB(R_base) m_R1; - analog::NETLIB_SUB(R_base) m_R2; - analog::NETLIB_SUB(R_base) m_R3; - analog::NETLIB_SUB(R_base) m_ROUT; - analog::NETLIB_SUB(R_base) m_RDIS; + NETLIB_HANDLERI(inputs) + { + // FIXME: assumes GND is connected to 0V. + + const auto reset = m_RESET(); + + const nl_fptype v_threshold = clamp_hl(m_R2().P()(), nlconst::magic(0.7), nlconst::magic(1.4)); + const nl_fptype v_trigger = clamp_hl(m_R2().N()(), nlconst::magic(0.7), nlconst::magic(1.4)); + + // avoid artificial oscillation due to overshoot compensation when + // the control input is used. + const auto overshoot_limit = std::min(m_overshoot_limit, std::max(0.0, (v_threshold - v_trigger) / 3.0)); + + if (!reset && m_last_reset) + { + m_ff = false; + } + else + { +#if (NL_USE_BACKWARD_EULER) + const bool threshold_exceeded = (m_THRES() + m_overshoot > v_threshold); + const bool trigger_exceeded = (m_TRIG() - m_overshoot > v_trigger); +#else + const bool threshold_exceeded = (m_THRES() + m_overshoot > v_threshold); + const bool trigger_exceeded = (m_TRIG() - m_undershoot > v_trigger); +#endif + if (!trigger_exceeded) + m_ff = true; + else if (threshold_exceeded) + { + m_ff = false; + } + } + + const bool out = (!reset ? false : m_ff); + + if (m_last_out && !out) + { +#if (NL_USE_BACKWARD_EULER) + m_overshoot += ((m_THRES() - v_threshold)) * 2.0; +#else + m_overshoot += ((m_THRES() - v_threshold)); +#endif + m_overshoot = plib::clamp(m_overshoot(), nlconst::zero(), overshoot_limit); + //if (this->name() == "IC6_2") + // printf("%f %s %f %f %f\n", exec().time().as_double(), this->name().c_str(), m_overshoot(), m_R2.P()(), m_THRES()); + m_RDIS().change_state([this]() + { + m_RDIS().set_R(nlconst::magic(R_ON)); + }); + m_OUT.push(m_R3().N()()); + } + else if (!m_last_out && out) + { +#if (NL_USE_BACKWARD_EULER) + m_overshoot += (v_trigger - m_TRIG()) * 2.0; + m_overshoot = plib::clamp(m_overshoot(), nlconst::zero(), overshoot_limit); +#else + m_undershoot += (v_trigger - m_TRIG()); + m_undershoot = plib::clamp(m_undershoot(), nlconst::zero(), overshoot_limit); +#endif + m_RDIS().change_state([this]() + { + m_RDIS().set_R(nlconst::magic(R_OFF)); + }); + // FIXME: Should be delayed by 100ns + m_OUT.push(m_R1().P()()); + } + m_last_reset = reset; + m_last_out = out; + } + NETLIB_SUB_NS(analog, R_base) m_R1; + NETLIB_SUB_NS(analog, R_base) m_R2; + NETLIB_SUB_NS(analog, R_base) m_R3; + NETLIB_SUB_NS(analog, R_base) m_ROUT; + NETLIB_SUB_NS(analog, R_base) m_RDIS; logic_input_t m_RESET; analog_input_t m_THRES; @@ -102,98 +145,19 @@ namespace netlist state_var<bool> m_last_out; state_var<bool> m_ff; state_var<bool> m_last_reset; + state_var<nl_fptype> m_overshoot; + state_var<nl_fptype> m_undershoot; + nl_fptype m_overshoot_limit; - nl_double clamp(const nl_double v, const nl_double a, const nl_double b) - { - nl_double ret = v; - nl_double vcc = m_R1.m_P(); - - if (ret > vcc - a) - ret = vcc - a; - if (ret < b) - ret = b; - return ret; - } - }; - - NETLIB_OBJECT_DERIVED(NE555_dip, NE555) - { - NETLIB_CONSTRUCTOR_DERIVED(NE555_dip, NE555) + nl_fptype clamp_hl(const nl_fptype v, const nl_fptype a, const nl_fptype b) noexcept { - register_subalias("1", "GND"); // Pin 1 - register_subalias("2", "TRIG"); // Pin 2 - register_subalias("3", "OUT"); // Pin 3 - register_subalias("4", "RESET"); // Pin 4 - register_subalias("5", "CONT"); // Pin 5 - register_subalias("6", "THRESH"); // Pin 6 - register_subalias("7", "DISCH"); // Pin 7 - register_subalias("8", "VCC"); // Pin 8 + const nl_fptype vcc = m_R1().P()(); + return plib::clamp(v, b, vcc - a); } }; - NETLIB_RESET(NE555) - { - m_R1.reset(); - m_R2.reset(); - m_R3.reset(); - m_ROUT.reset(); - m_RDIS.reset(); - - /* FIXME make resistances a parameter, properly model other variants */ - m_R1.set_R(5000); - m_R2.set_R(5000); - m_R3.set_R(5000); - m_ROUT.set_R(20); - m_RDIS.set_R(R_OFF); - - m_last_out = true; - } - - NETLIB_UPDATE(NE555) - { - // FIXME: assumes GND is connected to 0V. - - const auto reset = m_RESET(); - - if (!reset && m_last_reset) - { - m_ff = false; - } - else - { - const nl_double vt = clamp(m_R2.m_P(), 0.7, 1.4); - const bool bthresh = (m_THRES() > vt); - const bool btrig = (m_TRIG() > clamp(m_R2.m_N(), 0.7, 1.4)); - - if (!btrig) - m_ff = true; - else if (bthresh) - m_ff = false; - } - - const bool out = (!reset ? false : m_ff); - - if (m_last_out && !out) - { - m_RDIS.update(); - m_OUT.push(m_R3.m_N()); - m_RDIS.set_R(R_ON); - } - else if (!m_last_out && out) - { - m_RDIS.update(); - // FIXME: Should be delayed by 100ns - m_OUT.push(m_R1.m_P()); - m_RDIS.set_R(R_OFF); - } - m_last_reset = reset; - m_last_out = out; - } - NETLIB_DEVICE_IMPL(NE555, "NE555", "") - NETLIB_DEVICE_IMPL(NE555_dip, "NE555_DIP", "") NETLIB_DEVICE_IMPL_ALIAS(MC1455P, NE555, "MC1455P", "") - NETLIB_DEVICE_IMPL_ALIAS(MC1455P_dip, NE555_dip, "MC1455P_DIP", "") - } //namespace devices -} // namespace netlist + +} // namespace netlist::devices |