summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/devices/nld_ne555.cpp
blob: 5e990d2e97444cc4d7ae677e533ade449235ee4f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// license:GPL-2.0+
// 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"

#define R_OFF (1E20)
#define R_ON (25)   // Datasheet states a maximum discharge of 200mA, R = 5V / 0.2

namespace netlist
{
	namespace devices
	{
	NETLIB_OBJECT(NE555)
	{
		NETLIB_CONSTRUCTOR(NE555)
		, m_R1(*this, "R1")
		, m_R2(*this, "R2")
		, 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_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)
		{
			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_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);
		}

		NETLIB_UPDATEI();
		NETLIB_RESETI();

	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;

		logic_input_t m_RESET;
		analog_input_t m_THRES;
		analog_input_t m_TRIG;
		analog_output_t m_OUT;

		state_var<bool> m_last_out;
		state_var<bool> m_ff;
		state_var<bool> m_last_reset;

		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)
		{
			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
		}
	};

	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