]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // Copyright Paul A. Bristow 2016 |
2 | // Copyright John Z. Maddock 2016 | |
3 | ||
4 | // Distributed under the Boost Software License, Version 1.0. | |
5 | // (See accompanying file LICENSE_1_0.txt or | |
6 | // copy at http ://www.boost.org/LICENSE_1_0.txt). | |
7 | ||
8 | /*! \brief Graph showing use of Lambert W function to compute current | |
9 | through a diode-connected transistor with preset series resistance. | |
10 | ||
11 | \details T. C. Banwell and A. Jayakumar, | |
12 | Exact analytical solution of current flow through diode with series resistance, | |
13 | Electron Letters, 36(4):291-2 (2000). | |
14 | DOI: doi.org/10.1049/el:20000301 | |
15 | ||
16 | The current through a diode connected NPN bipolar junction transistor (BJT) | |
17 | type 2N2222 (See https://en.wikipedia.org/wiki/2N2222 and | |
18 | https://www.fairchildsemi.com/datasheets/PN/PN2222.pdf Datasheet) | |
19 | was measured, for a voltage between 0.3 to 1 volt, see Fig 2 for a log plot, showing a knee visible at about 0.6 V. | |
20 | ||
21 | The transistor parameter I sat was estimated to be 25 fA and the ideality factor = 1.0. | |
22 | The intrinsic emitter resistance re was estimated from the rsat = 0 data to be 0.3 ohm. | |
23 | ||
24 | The solid curves in Figure 2 are calculated using equation 5 with rsat included with re. | |
25 | ||
26 | http://www3.imperial.ac.uk/pls/portallive/docs/1/7292572.PDF | |
27 | ||
28 | */ | |
29 | ||
1e59de90 TL |
30 | #ifndef BOOST_MATH_STANDALONE |
31 | ||
92f5a8d4 TL |
32 | #include <boost/math/special_functions/lambert_w.hpp> |
33 | using boost::math::lambert_w0; | |
34 | #include <boost/math/special_functions.hpp> | |
35 | using boost::math::isfinite; | |
36 | #include <boost/svg_plot/svg_2d_plot.hpp> | |
37 | using namespace boost::svg; | |
38 | ||
39 | #include <iostream> | |
40 | // using std::cout; | |
41 | // using std::endl; | |
42 | #include <exception> | |
43 | #include <stdexcept> | |
44 | #include <string> | |
45 | #include <array> | |
46 | #include <vector> | |
47 | #include <utility> | |
48 | using std::pair; | |
49 | #include <map> | |
50 | using std::map; | |
51 | #include <set> | |
52 | using std::multiset; | |
53 | #include <limits> | |
54 | using std::numeric_limits; | |
55 | #include <cmath> // | |
56 | ||
57 | /*! | |
58 | Compute thermal voltage as a function of temperature, | |
59 | about 25 mV at room temperature. | |
60 | https://en.wikipedia.org/wiki/Boltzmann_constant#Role_in_semiconductor_physics:_the_thermal_voltage | |
61 | ||
62 | \param temperature Temperature (degrees Celsius). | |
63 | */ | |
64 | const double v_thermal(double temperature) | |
65 | { | |
1e59de90 TL |
66 | constexpr const double boltzmann_k = 1.38e-23; // joules/kelvin. |
67 | constexpr double charge_q = 1.6021766208e-19; // Charge of an electron (columb). | |
92f5a8d4 TL |
68 | double temp = +273; // Degrees C to K. |
69 | return boltzmann_k * temp / charge_q; | |
70 | } // v_thermal | |
71 | ||
72 | /*! | |
73 | Banwell & Jayakumar, equation 2, page 291. | |
74 | */ | |
75 | double i(double isat, double vd, double vt, double nu) | |
76 | { | |
77 | double i = isat * (exp(vd / (nu * vt)) - 1); | |
78 | return i; | |
79 | } // | |
80 | ||
81 | /*! | |
82 | Banwell & Jayakumar, Equation 4, page 291. | |
83 | i current flow = isat | |
84 | v voltage source. | |
85 | isat reverse saturation current in equation 4. | |
86 | (might implement equation 4 instead of simpler equation 5?). | |
87 | vd voltage drop = v - i* rs (equation 1). | |
88 | vt thermal voltage, 0.0257025 = 25 mV. | |
89 | nu junction ideality factor (default = unity), also known as the emission coefficient. | |
90 | re intrinsic emitter resistance, estimated to be 0.3 ohm from low current. | |
91 | rsat reverse saturation current | |
92 | ||
93 | \param v Voltage V to compute current I(V). | |
94 | \param vt Thermal voltage, for example 0.0257025 = 25 mV, computed from boltzmann_k * temp / charge_q; | |
95 | \param rsat Resistance in series with the diode. | |
f67539c2 | 96 | \param re Intrinsic emitter resistance (estimated to be 0.3 ohm from the Rs = 0 data) |
92f5a8d4 TL |
97 | \param isat Reverse saturation current (See equation 2). |
98 | \param nu Ideality factor (default = unity). | |
99 | ||
100 | \returns I amp as function of V volt. | |
101 | */ | |
102 | ||
103 | //[lambert_w_diode_graph_2 | |
104 | double iv(double v, double vt, double rsat, double re, double isat, double nu = 1.) | |
105 | { | |
106 | // V thermal 0.0257025 = 25 mV | |
107 | // was double i = (nu * vt/r) * lambert_w((i0 * r) / (nu * vt)); equ 5. | |
108 | ||
109 | rsat = rsat + re; | |
110 | double i = nu * vt / rsat; | |
111 | // std::cout << "nu * vt / rsat = " << i << std::endl; // 0.000103223 | |
112 | ||
113 | double x = isat * rsat / (nu * vt); | |
114 | // std::cout << "isat * rsat / (nu * vt) = " << x << std::endl; | |
115 | ||
116 | double eterm = (v + isat * rsat) / (nu * vt); | |
117 | // std::cout << "(v + isat * rsat) / (nu * vt) = " << eterm << std::endl; | |
118 | ||
119 | double e = exp(eterm); | |
120 | // std::cout << "exp(eterm) = " << e << std::endl; | |
121 | ||
122 | double w0 = lambert_w0(x * e); | |
123 | // std::cout << "w0 = " << w0 << std::endl; | |
124 | return i * w0 - isat; | |
125 | } // double iv | |
126 | ||
127 | //] [\lambert_w_diode_graph_2] | |
128 | ||
129 | ||
130 | std::array<double, 5> rss = { 0., 2.18, 10., 51., 249 }; // series resistance (ohm). | |
131 | std::array<double, 7> vds = { 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 }; // Diode voltage. | |
132 | std::array<double, 7> lni = { -19.65, -15.75, -11.86, -7.97, -4.08, -0.0195, 3.6 }; // ln(current). | |
133 | ||
134 | int main() | |
135 | { | |
136 | try | |
137 | { | |
138 | std::cout << "Lambert W diode current example." << std::endl; | |
139 | ||
140 | //[lambert_w_diode_graph_1 | |
141 | double nu = 1.0; // Assumed ideal. | |
142 | double vt = v_thermal(25); // v thermal, Shockley equation, expect about 25 mV at room temperature. | |
143 | double boltzmann_k = 1.38e-23; // joules/kelvin | |
144 | double temp = 273 + 25; | |
145 | double charge_q = 1.6e-19; // column | |
146 | vt = boltzmann_k * temp / charge_q; | |
147 | std::cout << "V thermal " << vt << std::endl; // V thermal 0.0257025 = 25 mV | |
148 | double rsat = 0.; | |
149 | double isat = 25.e-15; // 25 fA; | |
150 | std::cout << "Isat = " << isat << std::endl; | |
151 | double re = 0.3; // Estimated from slope of straight section of graph (equation 6). | |
152 | double v = 0.9; | |
153 | double icalc = iv(v, vt, 249., re, isat); | |
154 | std::cout << "voltage = " << v << ", current = " << icalc << ", " << log(icalc) << std::endl; // voltage = 0.9, current = 0.00108485, -6.82631 | |
155 | //] [/lambert_w_diode_graph_1] | |
156 | ||
157 | // Plot a few measured data points. | |
158 | std::map<const double, double> zero_data; // Extrapolated from slope of measurements with no external resistor. | |
159 | zero_data[0.3] = -19.65; | |
160 | zero_data[0.4] = -15.75; | |
161 | zero_data[0.5] = -11.86; | |
162 | zero_data[0.6] = -7.97; | |
163 | zero_data[0.7] = -4.08; | |
164 | zero_data[0.8] = -0.0195; | |
165 | zero_data[0.9] = 3.9; | |
166 | ||
167 | std::map<const double, double> measured_zero_data; // No external series resistor. | |
168 | measured_zero_data[0.3] = -19.65; | |
169 | measured_zero_data[0.4] = -15.75; | |
170 | measured_zero_data[0.5] = -11.86; | |
171 | measured_zero_data[0.6] = -7.97; | |
172 | measured_zero_data[0.7] = -4.2; | |
173 | measured_zero_data[0.72] = -3.5; | |
174 | measured_zero_data[0.74] = -2.8; | |
175 | measured_zero_data[0.76] = -2.3; | |
176 | measured_zero_data[0.78] = -2.0; | |
177 | // Measured from Fig 2 as raw data not available. | |
178 | ||
179 | double step = 0.1; | |
180 | for (int i = 0; i < vds.size(); i++) | |
181 | { | |
182 | zero_data[vds[i]] = lni[i]; | |
183 | std::cout << lni[i] << " " << vds[i] << std::endl; | |
184 | } | |
185 | step = 0.01; | |
186 | ||
187 | std::map<const double, double> data_2; | |
188 | for (double v = 0.3; v < 1.; v += step) | |
189 | { | |
190 | double current = iv(v, vt, 2., re, isat); | |
191 | data_2[v] = log(current); | |
192 | // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; | |
193 | } | |
194 | std::map<const double, double> data_10; | |
195 | for (double v = 0.3; v < 1.; v += step) | |
196 | { | |
197 | double current = iv(v, vt, 10., re, isat); | |
198 | data_10[v] = log(current); | |
199 | // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; | |
200 | } | |
201 | std::map<const double, double> data_51; | |
202 | for (double v = 0.3; v < 1.; v += step) | |
203 | { | |
204 | double current = iv(v, vt, 51., re, isat); | |
205 | data_51[v] = log(current); | |
206 | // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; | |
207 | } | |
208 | std::map<const double, double> data_249; | |
209 | for (double v = 0.3; v < 1.; v += step) | |
210 | { | |
211 | double current = iv(v, vt, 249., re, isat); | |
212 | data_249[v] = log(current); | |
213 | // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; | |
214 | } | |
215 | ||
216 | svg_2d_plot data_plot; | |
217 | ||
218 | data_plot.title("Diode current versus voltage") | |
219 | .x_size(400) | |
220 | .y_size(300) | |
221 | .legend_on(true) | |
222 | .legend_lines(true) | |
223 | .x_label("voltage (V)") | |
224 | .y_label("log(current) (A)") | |
225 | //.x_label_on(true) | |
226 | //.y_label_on(true) | |
227 | //.xy_values_on(false) | |
228 | .x_range(0.25, 1.) | |
229 | .y_range(-20., +4.) | |
230 | .x_major_interval(0.1) | |
231 | .y_major_interval(4) | |
232 | .x_major_grid_on(true) | |
233 | .y_major_grid_on(true) | |
234 | //.x_values_on(true) | |
235 | //.y_values_on(true) | |
236 | .y_values_rotation(horizontal) | |
237 | //.plot_window_on(true) | |
238 | .x_values_precision(3) | |
239 | .y_values_precision(3) | |
240 | .coord_precision(4) // Needed to avoid stepping on curves. | |
241 | .copyright_holder("Paul A. Bristow") | |
242 | .copyright_date("2016") | |
243 | //.background_border_color(black); | |
244 | ; | |
245 | ||
246 | // ₀ = subscript zero. | |
247 | data_plot.plot(zero_data, "I₀(V)").fill_color(lightgray).shape(none).size(3).line_on(true).line_width(0.5); | |
248 | data_plot.plot(measured_zero_data, "Rs=0 Ω").fill_color(lightgray).shape(square).size(3).line_on(true).line_width(0.5); | |
249 | data_plot.plot(data_2, "Rs=2 Ω").line_color(blue).shape(none).line_on(true).bezier_on(false).line_width(1); | |
250 | data_plot.plot(data_10, "Rs=10 Ω").line_color(purple).shape(none).line_on(true).bezier_on(false).line_width(1); | |
251 | data_plot.plot(data_51, "Rs=51 Ω").line_color(green).shape(none).line_on(true).line_width(1); | |
252 | data_plot.plot(data_249, "Rs=249 Ω").line_color(red).shape(none).line_on(true).line_width(1); | |
253 | data_plot.write("./diode_iv_plot"); | |
254 | ||
255 | // bezier_on(true); | |
256 | } | |
257 | catch (std::exception& ex) | |
258 | { | |
259 | std::cout << ex.what() << std::endl; | |
260 | } | |
261 | ||
262 | ||
263 | } // int main() | |
264 | ||
265 | /* | |
266 | ||
267 | //[lambert_w_output_1 | |
268 | Output: | |
269 | Lambert W diode current example. | |
270 | V thermal 0.0257025 | |
271 | Isat = 2.5e-14 | |
272 | voltage = 0.9, current = 0.00108485, -6.82631 | |
273 | -19.65 0.3 | |
274 | -15.75 0.4 | |
275 | -11.86 0.5 | |
276 | -7.97 0.6 | |
277 | -4.08 0.7 | |
278 | -0.0195 0.8 | |
279 | 3.6 0.9 | |
280 | ||
281 | //] [/lambert_w_output_1] | |
282 | */ | |
1e59de90 | 283 | #endif // BOOST_MATH_STANDALONE |