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