<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4092193417928690079</id><updated>2012-02-09T21:44:14.576+08:00</updated><title type='text'>On the Drawing Board</title><subtitle type='html'>adventures in electronic circuit design</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default?start-index=101&amp;max-results=100'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>108</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-2922878175880548010</id><published>2012-02-04T09:53:00.000+08:00</published><updated>2012-02-04T09:53:34.072+08:00</updated><title type='text'>Just having a little fun</title><content type='html'>This circuit was meant to test an idea but the idea turned out to be a dead end. So I ended up just playing with the circuit. Red, green and blue LEDs are turned on in sequence. Because of the difference in light intensity I've used different resistors values for each of the colors to try and even the output. Position of the potentiometer is read by the PIC16F1824's ADC. The two least significant bits of the 10-bit ADC output are ignored and the 8-bit value is copied to TMR0 and timer0 sets the amount of time each LED is on. &lt;br /&gt;&lt;br /&gt;The white cylinder over the LEDs is just an old milky-white colored 35mm film canister. But it makes a heck of difference on how the light output is seen by the human eye. Certainly far more interesting with it. By varying the direction in which the LEDs point different light patterns on the cylinder can be created. &lt;br /&gt;&lt;br /&gt;The MCU is run at 1MHz. LED switching occurs in the interrupt service routine. Maximum switching frequency is approximately 325Hz--each LED is on for just around 1ms. Minimum frequency is around 1.27Hz. Period T = on-time of each LED x number of LEDs. Frequency = 1/T. Timer0 is used for timing the LEDs. Its prescale is set to 256. With the clock running at 1MHz, time for one instruction cycle = 4/1MHz = 4µs. Timer0 tick = TMR0 x 256 x 4µs. The reset value of TMR0 can range from zero to 255 depending on the position of the potentiometer. &lt;br /&gt;&lt;br /&gt;Timer2 is used to periodically read the pot and store its value in a variable which then gets copied to TMR0 during every timer0 interrupt. &lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/G5WeqLoy4aM" width="480"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-nISlN95B6HQ/TyyPNQhm4pI/AAAAAAAABEw/dUU0T0sm_Ps/s1600/rgb-led-schem.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="247" src="http://2.bp.blogspot.com/-nISlN95B6HQ/TyyPNQhm4pI/AAAAAAAABEw/dUU0T0sm_Ps/s320/rgb-led-schem.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;#define  int1                bit&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;#define  int32               unsigned long&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;#define  _on                 0&lt;br /&gt;#define  _off                1&lt;br /&gt;&lt;br /&gt;#define  yes                 1&lt;br /&gt;#define  no                  0&lt;br /&gt;&lt;br /&gt;#define  input               1         // for TRISx&lt;br /&gt;#define  output              0         // for TRISx&lt;br /&gt;&lt;br /&gt;#define  analog              1         // for ANSELx&lt;br /&gt;#define  digital             0         // for ANSELx&lt;br /&gt;&lt;br /&gt;#define  lred                LATC.f0             // red led&lt;br /&gt;#define  lgreen              LATC.f1             // green led&lt;br /&gt;#define  lblue               LATC.f2             // blue led&lt;br /&gt;&lt;br /&gt;#define  tris_lred           TRISC.f0            // for setting LED pin as output&lt;br /&gt;#define  tris_lgreen         TRISC.f1            // for setting LED pin as output&lt;br /&gt;#define  tris_lblue          TRISC.f2            // for setting LED pin as output&lt;br /&gt;&lt;br /&gt;#define  an_lred             ANSELC.f0           // for setting LED pin as digital&lt;br /&gt;#define  an_lgreen           ANSELC.f1           // for setting LED pin as digital&lt;br /&gt;#define  an_lblue            ANSELC.f2           // for setting LED pin as digital&lt;br /&gt;&lt;br /&gt;#define  ch_pot              3                   // analog channel of potentiometer&lt;br /&gt;&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;//          global variables&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;int8 TMR0COUNT;&lt;br /&gt;&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;//          functions&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;void IniReg()&lt;br /&gt;{&lt;br /&gt;  OSCCON    = 0b1011000;     // internal clock = 1MHz&lt;br /&gt;&lt;br /&gt;  // set LED pins as output&lt;br /&gt;  tris_lred = 0;&lt;br /&gt;  tris_lgreen = 0;&lt;br /&gt;  tris_lblue = 0;&lt;br /&gt;  &lt;br /&gt;  // set LED pins as digital&lt;br /&gt;  an_lred = 0;&lt;br /&gt;  an_lgreen = 0;&lt;br /&gt;  an_lblue = 0;&lt;br /&gt;&lt;br /&gt;  lred = 1;                  // start off with red LED&lt;br /&gt;  &lt;br /&gt;  // Timer0 setup&lt;br /&gt;  OPTION_REG = 0b10000111;   // Weak pull ups disabled&lt;br /&gt;                             // timer0 uses internal clock,&lt;br /&gt;                             // prescaler assigned to timer0, prescaler = 1:256&lt;br /&gt;  TMR0 = 0;&lt;br /&gt;  INTCON.TMR0IE = 1;         // timer0 interrupt enable&lt;br /&gt;  &lt;br /&gt;  // Timer2 setup&lt;br /&gt;  // with a 1MHz clock, prescale = 1:16, postscale = 1:1, PR2 = 200&lt;br /&gt;  // timer2 tick = 250 * 64 / (1MHz/4) = 64ms&lt;br /&gt;  T2CON = 0b111;             // timer2 on, prescale = 1:64, postscale = 1:1&lt;br /&gt;  PR2 = 250;                 // timer2 counts up from zero until the value of PR2 and then resets&lt;br /&gt;&lt;br /&gt;  // Analog to Digital Coverter setup&lt;br /&gt;  ADCON0 = ch_pot &amp;lt;&amp;lt; 2;      // shift in analog channel to be made active&lt;br /&gt;  ADCON0.ADON = 1;           // turn on ADC&lt;br /&gt;  ADCON1 = 0;                // left justified, Fosc/2, Vss as negative reference, Vdd as positive reference&lt;br /&gt;&lt;br /&gt;  INTCON.GIE = 1;            // global interrupt enabled&lt;br /&gt;} // void IniReg()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* =========================================================================================================&lt;br /&gt;&lt;br /&gt;According to PIC12F1824 Silicon Errata sheet DS80510D the ADC unit in certain silicon revisions is buggy:&lt;br /&gt;&lt;br /&gt;"An ADC conversion may not complete under these conditions:&lt;br /&gt;1. When FOSC is greater than 8 MHz and it is the clock source used for the ADC converter.&lt;br /&gt;2. The ADC is operating from its dedicated internal FRC oscillator and &lt;br /&gt;the device is not in Sleep mode (any FOSC frequency). &lt;br /&gt;When this occurs, the ADC Interrupt Flag (ADIF) does not get set, the GO/DONE bit does not get cleared, &lt;br /&gt;and the conversion result does not get loaded into the ADRESH and ADRESL result registers."&lt;br /&gt;&lt;br /&gt;mikroC Pro compiler's Adc_Read() built-in ADC function uses the FRC oscillators o it cannot be used.&lt;br /&gt;&lt;br /&gt;The workaround used here is as per method 1 in the said errata:&lt;br /&gt;"Select the system clock, FOSC, as the ADC clock source &lt;br /&gt;and reduce the FOSC frequency to 8 MHz or less when performing ADC conversions."&lt;br /&gt;&lt;br /&gt;=========================================================================================================  */&lt;br /&gt;&lt;br /&gt;int8 ADC()&lt;br /&gt;{&lt;br /&gt;  ADCON0.GO = 1;                       // start ADC conversion&lt;br /&gt;  while (ADCON0.GO) ;                  // just wait until AD conversion is done&lt;br /&gt;  return ADRESH;                       // return only the 8 most significant bits of the 10-bit value&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void interrupt()&lt;br /&gt;{&lt;br /&gt;  if (INTCON.TMR0IF)&lt;br /&gt;  {&lt;br /&gt;    TMR0 = TMR0COUNT;&lt;br /&gt;    if (lred)&lt;br /&gt;    {&lt;br /&gt;      lgreen = on;&lt;br /&gt;      lred = off;&lt;br /&gt;    }&lt;br /&gt;    else if (lgreen)&lt;br /&gt;    {&lt;br /&gt;      lblue = on;&lt;br /&gt;      lgreen = off;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      lred = on;&lt;br /&gt;      lblue = off;&lt;br /&gt;    }&lt;br /&gt;    INTCON.TMR0IF = 0;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  IniReg();&lt;br /&gt;&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (PIR1.TMR2IF)&lt;br /&gt;    {&lt;br /&gt;      TMR0COUNT = ADC();&lt;br /&gt;      PIR1.TMR2IF = 0;&lt;br /&gt;    }&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-2922878175880548010?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/2922878175880548010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2012/02/just-having-little-fun.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2922878175880548010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2922878175880548010'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2012/02/just-having-little-fun.html' title='Just having a little fun'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/G5WeqLoy4aM/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-8403344191991516480</id><published>2012-01-27T13:58:00.000+08:00</published><updated>2012-01-27T13:58:35.849+08:00</updated><title type='text'>Toy traffic light--the next generation</title><content type='html'>Just minutes after I presented the &lt;a href="http://electromotiveforces.blogspot.com/2012/01/toy-traffic-light-using-signal-tower.html"&gt;signal tower traffic light&lt;/a&gt; to the kids last week and briefed them on how it works and how to change the go/stop time duration, the eldest told me I should put a digital countdown timer on top of the tower so they'll know how many seconds there are remaining before red turns green and green turns amber. I was dumbstruck. Why the heck didn't I think of that?! His suggestion is nothing short of brilliant. These kids are geniuses (of course I'm fracking biased!).&lt;br /&gt;&lt;br /&gt;So it was back to the drawing board for v2.0. My boss wants his traffic light by this week. Told him I can't since I need time to design the circuit and the pcb and write the software and think about how to mount the LED readouts and then finally put everything together. Working for 7-year olds is tough.&lt;br /&gt;&lt;br /&gt;Well, writing the firmware was a breeze. The state machine structure lends itself to easy modification. And in the process bulbs went off in my head and I added other features including a new mode wherein the red and amber lights alternate, with the same on-time as the flashing red and flashing amber modes of 0.8sec. And since a seven segment LED display is going to be used anyway why not put it to use even in modes other than normal traffic light mode. So during the flashing red, flashing amber, and alternating red and amber modes the LED will be showing various non numeric characters just to add to the light show. And most importantly I've dumped the use of the green bulb as feedback for the number of seconds for go/stop on-time. The LED now displays "1", "2"... up to "6" to indicate 5sec, 10sec,... 30sec of go/stop on-time. I could easily show the actual number of seconds on the display but I didn't because I think it would be good multiplication practice for the kids. At least they get to know by heart the products of 1 x 5, 2 x 5, ... up to 6 x 5.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-fJEb-sDYtg8/TyGBLD7K7DI/AAAAAAAABEc/Og-uQuTHTsY/s1600/traffic-light-v2-schem1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://2.bp.blogspot.com/-fJEb-sDYtg8/TyGBLD7K7DI/AAAAAAAABEc/Og-uQuTHTsY/s320/traffic-light-v2-schem1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-M03e9EFbA0I/TyGBKyZF8DI/AAAAAAAABEU/6sR_m2muj7M/s1600/traffic-light-v2-schem2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://4.bp.blogspot.com/-M03e9EFbA0I/TyGBKyZF8DI/AAAAAAAABEU/6sR_m2muj7M/s320/traffic-light-v2-schem2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Parts list:&lt;br /&gt;PS - AC to DC adapter 12VDC 1000mA output&lt;br /&gt;D1 - 1N5822 Schottky diode&lt;br /&gt;MCU - PIC16F1827 microcontroller&lt;br /&gt;VR - 78L05 voltage regulator&lt;br /&gt;7SEG1, 7SEG2 - seven segment LED display common anode&lt;br /&gt;Q1, Q2 - S9012 PNP transistors&lt;br /&gt;Q3, Q4 - 2N7000 n-channel enhancement MOSFET&lt;br /&gt;Q5, Q6, Q7 - TIP102 NPN Darlington transistor&lt;br /&gt;Q8 - ULN2003 transistor array&lt;br /&gt;&lt;br /&gt;My initial design had the LED powered from the 5V rail. This required using an L7805 which has an output capacity of at least 1000mA instead of the 78L05 which can only put out 100mA. Computations showed that because of the large headroom (12V - 5V) and large current draw (around 150mA) the power dissipation of the voltage regulator would raise its temperature close to 100 Celsius and thus necessitate a heatsink. Shudder! A heatsink eats up so much space. So it occurred to me to just power the LED directly off the unregulated supply of 12V. Of course the power dissipation issue would move over to the LED current limiting resistors but that is easily taken care of by employing 1/4-W resistors instead of 1/8-W. Trivial. In the initial 5V design it was a simple matter of using PNP transistors to provide power to the common anode seven segment LEDs. But with the emitter at 12V the base of the PNP can no longer be directly hooked up to the 5-volt MCU. In order to switch the PNP I've opted to use the 2N7000 MOSFET. Using an NPN would've been just as effective but it would've required the addition of a base current limiting resistor. The less components on the board the better. In order for the MOSFET and PNP setup to work properly the base of the PNP must be pulled up to the emitter voltage when the 2N7000 is off, else the base will be left floating. Thus, the 51Kohm resistor. When the 2N7000 is turned off the pull-up resistor brings the base voltage to 12V thus switching off the PNP and cutting supply to the LED display.&lt;br /&gt;&lt;br /&gt;I've decided to install two LED displays back-to-back both showing exactly the same readout. Each segment will be provided around 10mA. Because the current draw is double (two LED displays) and because of the 12VDC supply the MCU pins cannot be used to directly sink the current. Therefore a ULN2003 transistor array takes care of the power switching.&lt;br /&gt;&lt;br /&gt;The tens and ones digits on the display are multiplexed and switched every 4ms giving a frequency of 1/ (4ms x 2) = 125Hz. That's more than fast enough to provide a flicker-free display. &lt;br /&gt;&lt;br /&gt;Because of the additional function calls to update the LED display I've increased the the clock rate from the previous 500kHz to 2MHz. Timer2 tick is still 4ms. A check using the &lt;a href="http://www.saleae.com/logic/"&gt;Saleae Logic&lt;/a&gt; shows that at 2MHz it usually takes only about half a millisecond to go through all the functions. However, when go/stop on-time is changed the time consumed jumps to over 3ms. Measurement shows the &lt;code&gt;ComputeRedGreenTime()&lt;/code&gt; function takes 0.50ms. Multiplication does take time but I discovered the king of snails is the EEPROM write. Storing the one byte stop/go on-time takes a whopping 2.66ms. But this is actually better than the datasheet spec of 4.0ms typical and 5.0ms max. Given the half millisecond average (when the user is not selecting a stop/go time), I could actually have retained the 500kHz clock. The savings on power is, however, marginal and negligible compared to what the bulbs and LEDs consume.&lt;br /&gt;&lt;br /&gt;I'll post a vid and pics of the new and improved traffic light when I finally build the circuit and figure out how to mount the seven segment LED boards on top of the tower and still be able to have access to the screw of the tower's cap and protect the boards from the 7-, 6-, and 4-yr old demotion team!.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt;&lt;br /&gt;Kids' Traffic Light with Countdown Timer Using an Industrial Signal Tower&lt;br /&gt;&lt;br /&gt;Created:        January 2012&lt;br /&gt;Processor:      PIC 16F1827&lt;br /&gt;Compiler        mikroC Pro 5.0.0&lt;br /&gt;Configuration:  power up timer, brownout reset (set to 2.5V), WDT, stack over/underflow reset -- all enabled, all others disabled&lt;br /&gt;&lt;br /&gt;* Uses a 12VDC wall wart as power supply&lt;br /&gt;&lt;br /&gt;* Tower light 12VDC red, amber, green incandescent lamps are switched by NPN Darlington transistors&lt;br /&gt;&lt;br /&gt;* pushbutton:&lt;br /&gt;     * when momentarily pressed: cycles through different possible modes:&lt;br /&gt;          1. flashing amber&lt;br /&gt;          2. flashing red&lt;br /&gt;          3. alternating amber and red&lt;br /&gt;          4. normal traffic light: green -&gt; amber -&gt; red&lt;br /&gt;&lt;br /&gt;     * when pressed and kept depressed for over a couple of seconds LED display shows 1,2,3,4,5,6 in sequence corresponding to following go/stop on-time:&lt;br /&gt;          1. 5sec&lt;br /&gt;          2. 10sec&lt;br /&gt;          3. 15sec&lt;br /&gt;          4. 20sec&lt;br /&gt;          5. 25sec&lt;br /&gt;          6. 30sec&lt;br /&gt;     * although LED readout can be made to show number of real-time seconds when selecting go/stop time&lt;br /&gt;       showing it in the manner above provides an opportunity to teach the children how to multiply--in this case multiply by 5&lt;br /&gt;&lt;br /&gt;* go/stop on-time is stored in EEPROM&lt;br /&gt;&lt;br /&gt;* has 7-segment LED display&lt;br /&gt;     * shows the go/stop on-time when selecting it&lt;br /&gt;     * shows the remaining time before light changes&lt;br /&gt;     * shows various non-numeric characters in other modes&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       input / output&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  lred      LATB.f4                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;#define  lamber    LATA.f6                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;#define  lgreen    LATB.f5                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;&lt;br /&gt;#define  pb        PORTA.f5                      // momentary contact push button&lt;br /&gt;#define  tris_pb   TRISA.f5&lt;br /&gt;&lt;br /&gt;#define  anode_ones          LATA.f0             // S9012 PNP transistor&lt;br /&gt;#define  anode_tens          LATA.f1             // S9012 PNP transistor&lt;br /&gt;&lt;br /&gt;#define  seg_a               LATA.f2             // segment a of seven segment LED common anode&lt;br /&gt;#define  seg_b               LATA.f3             // segment b of seven segment LED common anode&lt;br /&gt;#define  seg_c               LATA.f4             // segment c of seven segment LED common anode&lt;br /&gt;#define  seg_d               LATB.f0             // segment d of seven segment LED common anode&lt;br /&gt;#define  seg_e               LATB.f1             // segment e of seven segment LED common anode&lt;br /&gt;#define  seg_f               LATB.f2             // segment f of seven segment LED common anode&lt;br /&gt;#define  seg_g               LATB.f3             // segment g of seven segment LED common anode&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       traffic light time duration&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  count_ini           250       // number of timer2 ticks to make one second&lt;br /&gt;&lt;br /&gt;#define  ambertime           500       // in normal mode -- amount of time for amber light to be on after green and before red -- time in terms of timer2 ticks&lt;br /&gt;#define  flashambertime      200       // in flashing amber mode -- amount of time for amber light to be on and amount of time to be off -- time in terms of timer2 ticks&lt;br /&gt;#define  flashredtime        200       // in flashing red mode -- amount of time for red light to be on and amount of time to be off -- time in terms of timer2 ticks&lt;br /&gt;#define  flashredambtime     200       // in red/amber alternate flashing mode -- amount of time for red light to be on and amount of time amber light to be on -- time in terms of timer2 ticks&lt;br /&gt;#define  ledflashtime        25        // in normal mode during yield (amber light) -- amount of time the character displayed on 7-seg is flashed on and off&lt;br /&gt;&lt;br /&gt;#define  changergtime        375       // minimum amount of time for button to be kept pressed before cycling through the green light on time -- time in terms of timer2 ticks&lt;br /&gt;                                       // given 4ms timer2 tick, changergtime of 250 = 1sec real time&lt;br /&gt;&lt;br /&gt;#define  prechangergtime     changergtime - 250  // amount of time after button is held down when all lights are turned off in preparation for possible change of green/red light on time&lt;br /&gt;&lt;br /&gt;#define  rgtime_increm       1250      // amount of time for green/red light to be on, and multiples thereof upon continual button press -- value in terms of timer2 ticks.&lt;br /&gt;#define  maxrgtime           6         // maximum allowed multiple of rgtime_increm&lt;br /&gt;&lt;br /&gt;int16 REDGREENTIME;                    // amount of time for green/red light to be on -- time in terms of timer2 ticks&lt;br /&gt;int8  RGTIME;                          // multiples of rgtime_increm such that RGTIME*rgtime_increm = REDGREENTIME&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       for pushbutton&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  rising              1         // rising edge detected. used by PBedge&lt;br /&gt;#define  released            1         // rising edge detected. used by PBedge&lt;br /&gt;#define  falling             2         // falling edge detected. used by PBedge&lt;br /&gt;#define  pressed             2         // falling edge detected. used by PBedge&lt;br /&gt;#define  none                0         // no edge. used by PBedge&lt;br /&gt;&lt;br /&gt;int8 PBval;                            // last eight values of the switch upon reading it&lt;br /&gt;int8 PBedge;                           // edge detected?, 0 = no edge detect, 1 = rising edge, 2 = falling edge; other values = Not Used / Undefined for now&lt;br /&gt;bit PBlevel;                           // voltage level of switch when not bouncing (hi = 1, lo = 0)&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       general defines and variables&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  addr_rgtime         0x10      // eeprom address for user selected green/red light on time&lt;br /&gt;&lt;br /&gt;#define  disp_dash           111       // code to display a dash, for use with UpdateDisp()&lt;br /&gt;&lt;br /&gt;#define  disp_3horiz         120       // code to display three horizontal segments, for use with UpdateDisp()&lt;br /&gt;#define  disp_tophoriz       121       // code to display top horizontal segment, for use with SegmentAssign()&lt;br /&gt;#define  disp_midhoriz       122       // code to display middle horizontal segment, for use with SegmentAssign()&lt;br /&gt;#define  disp_botthoriz      123       // code to display bottom horizontal segment, for use with SegmentAssign()&lt;br /&gt;&lt;br /&gt;#define  disp_brackets       130       // code to display brackets, for use with UpdateDisp()&lt;br /&gt;#define  disp_left_bracket   131       // code to display left bracket, for use with SegmentAssign()&lt;br /&gt;#define  disp_right_bracket  132       // code to display right bracket, for use with SegmentAssign()&lt;br /&gt;&lt;br /&gt;#define  disp_topsquare      141       // code to display top square, for use with UpdateDisp()&lt;br /&gt;#define  disp_bottsquare     142       // code to display bottom square, for use with UpdateDisp()&lt;br /&gt;&lt;br /&gt;#define  disp_blank          255       // code to display nothing, for use with UpdateDisp()&lt;br /&gt;&lt;br /&gt;int16 TIME = 0;                        // records how long a light has been on  -- in terms of timer2 ticks&lt;br /&gt;int8  VALUE = disp_blank;              // number or character to be displayed on 7-seg LED&lt;br /&gt;int8  RGSECONDS;                       // user-selected red/green on-time in real-time seconds&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define  int1                bit&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;#define  int32               unsigned long&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;#define  _on                 0&lt;br /&gt;#define  _off                1&lt;br /&gt;&lt;br /&gt;#define  yes                 1&lt;br /&gt;#define  no                  0&lt;br /&gt;&lt;br /&gt;#define  input               1         // for TRISx&lt;br /&gt;#define  output              0         // for TRISx&lt;br /&gt;&lt;br /&gt;#define  analog              1         // for ANSELx&lt;br /&gt;#define  digital             0         // for ANSELx&lt;br /&gt;&lt;br /&gt;#define  hi                  1         // switch level high&lt;br /&gt;#define  lo                  0         // switch level low&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       for state machines&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;// !! A C H T U N G !! &lt;br /&gt;// make sure all non user selectable modes such as _standby and _selectrgtime come AFTER user selectable&lt;br /&gt;// and that _normal" is the last item in the valid user selectable modes because it is used as the max value in ProcessKey()&lt;br /&gt;enum {_flashingamber, _flashingred, _flashingredamber, _normal, &lt;br /&gt;      /* the modes that follow are non-user selectable --&gt; */ _standby, _selectrgtime} STATEMODE = _flashingamber;&lt;br /&gt;&lt;br /&gt;enum {_stop, _yield_ini, _yield, _go} STATENORMAL = _stop;&lt;br /&gt;enum {_init, _flash} STATEFLASHGREEN = _init;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ===========================================================================================&lt;br /&gt;//       LED 7-Segment Display&lt;br /&gt;// ===========================================================================================&lt;br /&gt;&lt;br /&gt;void SegmentAssign(int8 num)&lt;br /&gt;{&lt;br /&gt;  seg_a = 0;&lt;br /&gt;  seg_b = 0;&lt;br /&gt;  seg_c = 0;&lt;br /&gt;  seg_d = 0;&lt;br /&gt;  seg_e = 0;&lt;br /&gt;  seg_f = 0;&lt;br /&gt;  seg_g = 0;&lt;br /&gt;&lt;br /&gt;  switch (num)&lt;br /&gt;  {&lt;br /&gt;    case 1:&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 2:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 3:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 4:&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 5:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 6:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 7:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 8:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 9:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 0:&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_dash:          // displays a dash - middle horizontal segment&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_3horiz:        // displays three horizontal segments&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_tophoriz:      // displays top horizontal segment&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_botthoriz:     // displays bottom horizontal segment&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_left_bracket:  // displays opening bracket&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_right_bracket: // displays closing bracket&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_topsquare:     // displays a square on upper half of LED&lt;br /&gt;      seg_a = 1;&lt;br /&gt;      seg_b = 1;&lt;br /&gt;      seg_f = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case disp_bottsquare:    // displays a square on the lower half of the LED&lt;br /&gt;      seg_c = 1;&lt;br /&gt;      seg_d = 1;&lt;br /&gt;      seg_e = 1;&lt;br /&gt;      seg_g = 1;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:                 // if invalid value then display blank&lt;br /&gt;&lt;br /&gt;    case disp_blank:         // segments have already been turned off at the start of this function so do nothing&lt;br /&gt;      break;&lt;br /&gt;  } // switch (num)&lt;br /&gt;} // void SegmentAssign(int8 num)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void UpdateDisp()&lt;br /&gt;{&lt;br /&gt;  static bit anode;          // flag bit multiplexing display between tens and ones place&lt;br /&gt;  int8 TENS, ONES;           // contains the digit or code of the non-numeric character to be displayed in the tens and ones place&lt;br /&gt;&lt;br /&gt;  // turn off power to 7-segment LED display.&lt;br /&gt;  anode_ones = off;&lt;br /&gt;  anode_tens = off;&lt;br /&gt;&lt;br /&gt;  if (STATEMODE != _standby)         // turn on LED display only when not in _standby mode&lt;br /&gt;  {&lt;br /&gt;    if (VALUE &lt; 100)                 // values &gt;= 100 are codes for non-numeric characters&lt;br /&gt;    {&lt;br /&gt;      TENS = VALUE/10;&lt;br /&gt;      ONES = VALUE%10;&lt;br /&gt;    } // if (VALUE &lt; 100)&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      switch (VALUE)&lt;br /&gt;      {&lt;br /&gt;        case disp_dash:&lt;br /&gt;          TENS = disp_dash;&lt;br /&gt;          ONES = disp_dash;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_3horiz:&lt;br /&gt;          TENS = disp_3horiz;&lt;br /&gt;          ONES = disp_3horiz;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_tophoriz:&lt;br /&gt;          TENS = disp_tophoriz;&lt;br /&gt;          ONES = disp_tophoriz;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_botthoriz:&lt;br /&gt;          TENS = disp_botthoriz;&lt;br /&gt;          ONES = disp_botthoriz;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_brackets:&lt;br /&gt;          TENS = disp_left_bracket;&lt;br /&gt;          ONES = disp_right_bracket;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_topsquare:&lt;br /&gt;          TENS = disp_topsquare;&lt;br /&gt;          ONES = disp_topsquare;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case disp_bottsquare:&lt;br /&gt;          TENS = disp_bottsquare;&lt;br /&gt;          ONES = disp_bottsquare;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:                       // if invalid value then display blank&lt;br /&gt;&lt;br /&gt;        case disp_blank:&lt;br /&gt;          TENS = disp_blank;&lt;br /&gt;          ONES = disp_blank;&lt;br /&gt;          break;&lt;br /&gt;      } // switch (VALUE)&lt;br /&gt;    } // else if (VALUE &gt;= 100)&lt;br /&gt;&lt;br /&gt;    if (anode)&lt;br /&gt;    {&lt;br /&gt;      if (TENS != 0)                   // do not display tens place if it's zero&lt;br /&gt;      {&lt;br /&gt;        SegmentAssign(TENS);&lt;br /&gt;        anode_tens = on;&lt;br /&gt;      }&lt;br /&gt;      anode = 0;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      SegmentAssign(ONES);&lt;br /&gt;      anode_ones = on;&lt;br /&gt;      anode = 1;&lt;br /&gt;    }&lt;br /&gt;  } // if (display)&lt;br /&gt;} // void UpdateDisp()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ===========================================================================================&lt;br /&gt;//       functions&lt;br /&gt;// ===========================================================================================&lt;br /&gt;&lt;br /&gt;void ComputeRedGreenTime()&lt;br /&gt;{&lt;br /&gt;  REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;  RGSECONDS = RGTIME*5;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IniReg()&lt;br /&gt;{&lt;br /&gt;  TRISA = output;&lt;br /&gt;  TRISB = output;&lt;br /&gt;  ANSELA = digital;&lt;br /&gt;  ANSELB = digital;&lt;br /&gt;&lt;br /&gt;  tris_pb = input;&lt;br /&gt;&lt;br /&gt;  // enable weak pull up for pushbutton and unused pins&lt;br /&gt;  OPTION_REG.NOT_WPUEN = 0;&lt;br /&gt;  WPUA = 0xFF;&lt;br /&gt;  // WPUB = wpullup;&lt;br /&gt;&lt;br /&gt;  // set internal clock frequency to 2MHz&lt;br /&gt;  OSCCON = 0b1100000;&lt;br /&gt;&lt;br /&gt;  // Timer2 is used for state machine and switch debouncing timing tick&lt;br /&gt;  // with clock = 2MHz, PR2 = 125, prescale = 1:16, postscale = 1:1&lt;br /&gt;  // TMR2 will count from zero to PR2 and timer2 interrupt occurs every 125*16 / (2MHz / 4) = 4ms = timer2 tick&lt;br /&gt;  T2CON = 0b110;             // postscaler = 1:1, prescaler = 1:16, timer2 on&lt;br /&gt;  TMR2 = 0;&lt;br /&gt;  PR2 = 125;&lt;br /&gt;&lt;br /&gt;  WDTCON = 0b1000;           // prescale = 1:512 (16ms typical)&lt;br /&gt;                             // WDTE in configuration word is configured so that WDT enabled when MCU awake and disabled when MCU asleep&lt;br /&gt;&lt;br /&gt;  // retrieve stored green/red light on time value.&lt;br /&gt;  // If eeprom-stored value is out of valid range then set it to minimum and store this value in eeprom&lt;br /&gt;  // stored values are in terms of multiples of rgtime_increm such that stored value multiplied by rgtime_increm = time in terms of TMR2 ticks&lt;br /&gt;  RGTIME = EEPROM_Read(addr_rgtime);&lt;br /&gt;  if (RGTIME == 0 || RGTIME &gt; maxrgtime)&lt;br /&gt;  {&lt;br /&gt;    RGTIME = 1;&lt;br /&gt;    EEPROM_Write(addr_rgtime, RGTIME);&lt;br /&gt;  }&lt;br /&gt;  ComputeRedGreenTime();                     // REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;&lt;br /&gt;  // initialize push button&lt;br /&gt;  PBval = 0xFF;&lt;br /&gt;  PBlevel = hi;&lt;br /&gt;} // void InitRegisters()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// amber flashes on and off at a rate determined by flashambertime&lt;br /&gt;// LED display shows the character stored in VALUE&lt;br /&gt;void FlashingAmber()&lt;br /&gt;{&lt;br /&gt;  static bit flag;&lt;br /&gt;&lt;br /&gt;  if (++TIME &lt; flashambertime)&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;    {&lt;br /&gt;      lamber = on;&lt;br /&gt;      VALUE = disp_brackets;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      lamber = off;&lt;br /&gt;      VALUE = disp_blank;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      flag = 0;&lt;br /&gt;    else&lt;br /&gt;      flag = 1;&lt;br /&gt;    TIME = 0;&lt;br /&gt;  }&lt;br /&gt;} // void FlashingAmber()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// red flashes on and off at a rate determined by flashredtime&lt;br /&gt;// LED display shows the character stored in VALUE&lt;br /&gt;void FlashingRed()&lt;br /&gt;{&lt;br /&gt;  static bit flag;&lt;br /&gt;&lt;br /&gt;  if (++TIME &lt; flashredtime)&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;    {&lt;br /&gt;      lred = on;&lt;br /&gt;      VALUE = disp_brackets;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      lred = off;&lt;br /&gt;      VALUE = disp_blank;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      flag = 0;&lt;br /&gt;    else&lt;br /&gt;      flag = 1;&lt;br /&gt;    TIME = 0;&lt;br /&gt;  }&lt;br /&gt;} // void FlashingRed()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// red and amber turn on alternately at a rate determined by flashredambertime&lt;br /&gt;// LED display shows the character stored in VALUE&lt;br /&gt;void FlashingRedAmber()&lt;br /&gt;{&lt;br /&gt;  static bit flag;&lt;br /&gt;&lt;br /&gt;  if (++TIME &lt; flashredambtime)&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;    {&lt;br /&gt;      lamber = off;&lt;br /&gt;      lred = on;&lt;br /&gt;      VALUE = disp_topsquare;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      lred = off;&lt;br /&gt;      lamber = on;&lt;br /&gt;      VALUE = disp_bottsquare;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      flag = 0;&lt;br /&gt;    else&lt;br /&gt;      flag = 1;&lt;br /&gt;    TIME = 0;&lt;br /&gt;  }&lt;br /&gt;} // void FlashingRedAmber()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// normal traffic light operation: green --&gt; amber --&gt; red&lt;br /&gt;// red/green light on-time is user selectable&lt;br /&gt;// LED display shows the countdown time in seconds during red and green light &lt;br /&gt;// LED display shows a flashing "--" during amber light, blink rate determined by ledbflashtime&lt;br /&gt;void Normal()&lt;br /&gt;{&lt;br /&gt;  static int8 COUNTER;&lt;br /&gt;  static bit toggle;&lt;br /&gt;&lt;br /&gt;  switch (STATENORMAL)&lt;br /&gt;  {&lt;br /&gt;    case _stop:&lt;br /&gt;      if (++TIME &lt; REDGREENTIME)&lt;br /&gt;      {&lt;br /&gt;        lred = on;&lt;br /&gt;        if (--COUNTER == 0)&lt;br /&gt;        {&lt;br /&gt;          --VALUE;&lt;br /&gt;          COUNTER = count_ini;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _go;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        COUNTER = count_ini;&lt;br /&gt;        VALUE = RGSECONDS;&lt;br /&gt;        lred = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _yield_ini:&lt;br /&gt;      toggle = 1;&lt;br /&gt;      VALUE = disp_dash;&lt;br /&gt;      COUNTER = ledflashtime;&lt;br /&gt;      TIME = 0;&lt;br /&gt;      STATENORMAL = _yield;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _yield:&lt;br /&gt;      if (++TIME &lt; ambertime)&lt;br /&gt;      {&lt;br /&gt;        lamber = on;&lt;br /&gt;&lt;br /&gt;        if (--COUNTER == 0)&lt;br /&gt;        {&lt;br /&gt;          if (toggle)&lt;br /&gt;          {&lt;br /&gt;            VALUE = disp_blank;&lt;br /&gt;            toggle = 0;&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;            VALUE = disp_dash;&lt;br /&gt;            toggle = 1;&lt;br /&gt;          }&lt;br /&gt;          COUNTER = ledflashtime;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _stop;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        COUNTER = count_ini;&lt;br /&gt;        VALUE = RGSECONDS;&lt;br /&gt;        lamber = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _go:&lt;br /&gt;      if (++TIME &lt; REDGREENTIME)&lt;br /&gt;      {&lt;br /&gt;        lgreen = on;&lt;br /&gt;        if (--COUNTER == 0)&lt;br /&gt;        {&lt;br /&gt;          --VALUE;&lt;br /&gt;          COUNTER = count_ini;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _yield_ini;&lt;br /&gt;        lgreen = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      TIME = 0;&lt;br /&gt;      STATENORMAL = _yield;&lt;br /&gt;  } // switch (STATENORMAL)&lt;br /&gt;} // void StateMachNormal()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void StateMachMain()&lt;br /&gt;{&lt;br /&gt;  switch (STATEMODE)&lt;br /&gt;  {&lt;br /&gt;    case _normal:&lt;br /&gt;      Normal();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flashingamber:&lt;br /&gt;      FlashingAmber();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flashingred:&lt;br /&gt;      FlashingRed();&lt;br /&gt;      break;&lt;br /&gt;      &lt;br /&gt;    case _flashingredamber:&lt;br /&gt;      FlashingRedAmber();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _standby:           // do nothing mode where all bulbs are off. used when changing red/green on-time&lt;br /&gt;      break;&lt;br /&gt;      &lt;br /&gt;    case _selectrgtime:      // er uhhh, I guess we do nothing as well&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      STATEMODE = _normal;&lt;br /&gt;  }&lt;br /&gt;} // void StateMachMain()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void AllLightsOff()&lt;br /&gt;{&lt;br /&gt;  lred = off;&lt;br /&gt;  lamber = off;&lt;br /&gt;  lgreen = off;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void DebounceSwitch()&lt;br /&gt;{&lt;br /&gt;  // shift all bits to the left&lt;br /&gt;  // if switch reading is hi then let pb_val bit 0 = 1&lt;br /&gt;  PBval &lt;&lt;= 1;&lt;br /&gt;  if (pb)&lt;br /&gt;    ++PBval;&lt;br /&gt;&lt;br /&gt;  PBedge = none;&lt;br /&gt;&lt;br /&gt;  // if level is lo and all bits of pb_val are now hi then&lt;br /&gt;  // a rising edge has been detected&lt;br /&gt;  // switch is considered just released when rising edge is detected&lt;br /&gt;  // switch level is now hi&lt;br /&gt; if ((!PBlevel) &amp;&amp; (PBval == 0xFF))&lt;br /&gt;  {&lt;br /&gt;    PBlevel = hi;&lt;br /&gt;    PBedge = rising;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // if level is hi and all bits of pb_val are now low then&lt;br /&gt;  // a falling edge has been detected&lt;br /&gt;  // switch is considered just pressed when falling edge is detected&lt;br /&gt;  // switch level is now lo&lt;br /&gt;  if ((PBlevel) &amp;&amp; (!PBval))&lt;br /&gt;  {&lt;br /&gt;    PBlevel = lo;&lt;br /&gt;    PBedge = falling;&lt;br /&gt;  }&lt;br /&gt;} // void DebounceSwitch()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void ProcessKey()&lt;br /&gt;{&lt;br /&gt;  int8 MODE;                           // temporary storage of current STATEMODE&lt;br /&gt;&lt;br /&gt;  static  int16  PBPRESSTIMETOTAL = 0; // Keeps track of how long PB is depressed in terms of timer2 ticks&lt;br /&gt;                                       // Keeps track of total time from falling edge (switched pressed) to rising edge (switch released).&lt;br /&gt;                                       // if less than changergtime then we know the keypress is momentary and user wants to change modes&lt;br /&gt;&lt;br /&gt;  static  int16  PBPRESSTIME = 0;      // Keeps track of how long PB is depressed in terms of timer2 ticks&lt;br /&gt;                                       // this variable is reset every time it exceeds changergtime&lt;br /&gt;                                       // every time it exceeds changergtime red/green on-time is incremented until masrgtime and then it cycles back to one&lt;br /&gt;&lt;br /&gt;  DebounceSwitch();&lt;br /&gt;  MODE = STATEMODE;&lt;br /&gt;&lt;br /&gt;  if (PBlevel == lo)&lt;br /&gt;  {&lt;br /&gt;    if (++PBPRESSTIMETOTAL == prechangergtime)&lt;br /&gt;    {&lt;br /&gt;      AllLightsOff();&lt;br /&gt;      STATEMODE = _standby;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (++PBPRESSTIME &gt;= changergtime)&lt;br /&gt;    {&lt;br /&gt;      PBPRESSTIME = 0;&lt;br /&gt;      if (++RGTIME &gt; maxrgtime)&lt;br /&gt;        RGTIME = 1;&lt;br /&gt;      EEPROM_Write(addr_rgtime, RGTIME);&lt;br /&gt;      ComputeRedGreenTime();                     // REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;      STATEMODE = _selectrgtime;&lt;br /&gt;      VALUE = RGTIME;                            // RGTIME will be displayed on the LED readout&lt;br /&gt;    } // if (++PBPRESSTIME &gt;= changergtime)&lt;br /&gt;  } // if (PBlevel == lo)&lt;br /&gt;&lt;br /&gt;  if (PBedge == released)&lt;br /&gt;  {&lt;br /&gt;    if (PBPRESSTIMETOTAL &lt; changergtime)         // if pushbutton was only momentarily pressed then change to the next mode&lt;br /&gt;    {&lt;br /&gt;      STATEMODE = ++MODE;&lt;br /&gt;      if (STATEMODE == _normal)&lt;br /&gt;        STATENORMAL = _yield_ini;                // this is necessary to start normal traffic light mode properly&lt;br /&gt;      else if (STATEMODE &gt; _normal)&lt;br /&gt;        STATEMODE = 0;&lt;br /&gt;    } // if (PBPRESSTIMETOTAL &lt; changergtime)&lt;br /&gt;    else  // green/red light on time has been changed so go to normal traffic light mode and begin with amber light&lt;br /&gt;    {&lt;br /&gt;      STATEMODE = _normal;&lt;br /&gt;      STATENORMAL = _yield_ini;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    AllLightsOff();          // turn off all bulbs and let state machine take care of which ones to turn on&lt;br /&gt;    TIME = 0;                // reset timer so that whatever mode has been selected, light will go through full time allotted&lt;br /&gt;    PBPRESSTIMETOTAL = 0;&lt;br /&gt;    PBPRESSTIME = 0;&lt;br /&gt;  } // if (PBedge == released)&lt;br /&gt;} // void ProcessKey()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  IniReg();&lt;br /&gt;&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (PIR1.TMR2IF)&lt;br /&gt;    {&lt;br /&gt;      PIR1.TMR2IF = 0;&lt;br /&gt;      asm{clrwdt}&lt;br /&gt;      UpdateDisp();&lt;br /&gt;      ProcessKey();&lt;br /&gt;      StateMachMain();&lt;br /&gt;    } // if (PIR1.TMR2IF)&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-8403344191991516480?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/8403344191991516480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/toy-traffic-light-next-generation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8403344191991516480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8403344191991516480'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/toy-traffic-light-next-generation.html' title='Toy traffic light--the next generation'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-fJEb-sDYtg8/TyGBLD7K7DI/AAAAAAAABEc/Og-uQuTHTsY/s72-c/traffic-light-v2-schem1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-6480657943441691692</id><published>2012-01-27T00:12:00.000+08:00</published><updated>2012-01-27T00:12:53.510+08:00</updated><title type='text'>Optimizing print quality of PCB artwork on transparency films</title><content type='html'>Over the past eight months or so I've tried three different transparency films (most probably made by different manufacturers). Joy Transparency Film comes in a box of 50 sheets. Inside every 5 sheets has its own plastic envelope. The label says the films each have a paper backing, but unfortunately there's none. There is no information on the packaging as to manufacturer and country of origin, even if "Japan" is prominently printed at the upper right hand corner of the plastic bag and "Japan Standard" on the bottom right. Entering "Joy transparency film" and "OF280" (its product number) on Google doesn't turn up anything. Price per sheet is approximately USD0.34.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qc42ttjGuqQ/TuRU8M0drdI/AAAAAAAAA_0/5xsoZFH5Ykg/s1600/joy-transparency-film-box.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="241" src="http://4.bp.blogspot.com/-qc42ttjGuqQ/TuRU8M0drdI/AAAAAAAAA_0/5xsoZFH5Ykg/s320/joy-transparency-film-box.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.fullmark.com/"&gt;Fullmark&lt;/a&gt; TPICL50 (Made in EU) comes in a black plastic bag containing 50 sheets. Searching the Fullmark website fails to turn up this particular product. In fact Fullmark doesn't seem to carry transparencies anymore (or they haven't updated their website). As with the Joy brand, Fullmark films don't have a paper backing. Price is around USD0.33/sheet. &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-HlFcjjzjRNs/TuRUBp1JH-I/AAAAAAAAA_c/NM3sxvWDvwY/s1600/fullmark-transparency-film-front.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-HlFcjjzjRNs/TuRUBp1JH-I/AAAAAAAAA_c/NM3sxvWDvwY/s320/fullmark-transparency-film-front.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rwSBTbun7dM/TuRUBx6WHlI/AAAAAAAAA_o/PI2iEqz2OYY/s1600/fullmark-transparency-film-back.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-rwSBTbun7dM/TuRUBx6WHlI/AAAAAAAAA_o/PI2iEqz2OYY/s320/fullmark-transparency-film-back.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The last type of film has no brand and has been repacked by the office supply store from which I bought it. This one has a paper backing and is the most expensive at USD0.60/sheet.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Of the three only Joy films produce very good quality printouts. The other two show substantial crazing of the ink. Fullmark is the worst offender. Depending on the printer settings it can border on being useless. Crazing can be detrimental since the cracks allow light to pass through and expose the photosensitive layer of the pcb.&lt;br /&gt;&lt;br /&gt;Having found a transparency film that is near optimal I needed to find out what printer settings would provide the most opaque printout and highest ink density. I could've varied the settings and compared the results but fortunately while searching for transparency brands I stumbled upon the recommendations for the &lt;a href="http://www.megauk.com/datasheets/5306_Data_Sheet_JetStar_Std_&amp;amp;_Premium_Inst.%280911%29.pdf"&gt;JetStar Inkjet Artwork Film:&lt;/a&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;3. Check printer settings. Make sure printer is set for the highest resolution and print quality, with High Speed Setting OFF. Note that the higher the resolution, the slower the print. Next, set the Media Setting to Matte, Heavyweight Matte, Archival Matte or Inkjet Backlight Film. &lt;b&gt;For Epson Ink Jet Printers&lt;/b&gt;, &lt;b&gt;set the Brightness control slider to +25&lt;/b&gt; and although a black print &lt;b&gt;always print in colour Mode&lt;/b&gt;. Options will vary based on printer and software available. Then, set ink deposit for optimum results. TIP: Experiment with the setting options available with your printer to determine the best set-up for your requirements. If positive seems light or lacks density, try increasing the print quality or amount of ink deposit. [emphasis added]&lt;/blockquote&gt;I tried those settings and it turns out that on the &lt;a href="http://www.epson.com.sg/epson_singapore/printers_and_all_in_ones/inkjet/product.page?product_name=Epson_Stylus_T10"&gt;Epson T10&lt;/a&gt; inkjet printer leaving "print in black ink only" &lt;i&gt;un&lt;/i&gt;selected and setting all the sliders to maximum (+25) actually gives a better ink density than printing in black only (I used to tick the print in black only option). Here are screenshots showing the settings I now currently use for the T10 .&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-_rDMjTJUnTQ/TyFsoJlXmQI/AAAAAAAABDw/qcZSNlBLaQY/s1600/Epson%2BT10%2Bmenu%2Bmain.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-_rDMjTJUnTQ/TyFsoJlXmQI/AAAAAAAABDw/qcZSNlBLaQY/s320/Epson%2BT10%2Bmenu%2Bmain.png" width="280" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-IS3w-Ioej2Y/TyFssRWxPqI/AAAAAAAABD8/VoELP7OTopA/s1600/Epson%2BT10%2Bmenu%2Badvanced.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-IS3w-Ioej2Y/TyFssRWxPqI/AAAAAAAABD8/VoELP7OTopA/s320/Epson%2BT10%2Bmenu%2Badvanced.png" width="280" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-buKKbr2HUQA/TyFsw8hmwUI/AAAAAAAABEI/Mj1F2dCiM5c/s1600/Epson%2BT10%2Bmenu%2Bcolor%2Bsettings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="301" src="http://1.bp.blogspot.com/-buKKbr2HUQA/TyFsw8hmwUI/AAAAAAAABEI/Mj1F2dCiM5c/s320/Epson%2BT10%2Bmenu%2Bcolor%2Bsettings.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The same settings as in the screenshots:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Advanced&lt;br /&gt;  Paper and Quality Options&lt;br /&gt;    Epson Matte&lt;br /&gt;    Best Photo&lt;br /&gt;    [paper size as required]&lt;br /&gt;  Print Options&lt;br /&gt;    Fix Red-Eye - uncheck&lt;br /&gt;    High Speed - uncheck&lt;br /&gt;    Edge Smoothing - check&lt;br /&gt;    Print Preview - check&lt;br /&gt;    Black Ink Only - uncheck&lt;br /&gt;  Color Management&lt;br /&gt;    Color Controls - select&lt;br /&gt;  Color Mode&lt;br /&gt;    Epson Vivid&lt;br /&gt;    Settings:&lt;br /&gt;      Brightness: +25 (max) &lt;br /&gt;      Contrast: +25 (max)&lt;br /&gt;      Saturation: +25 (max)&lt;br /&gt;      Density: +25 (max)&lt;br /&gt;      Horizontal: 0&lt;br /&gt;      Vertical: 0&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-6480657943441691692?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/6480657943441691692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/optimizing-print-quality-of-pcb-artwork.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6480657943441691692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6480657943441691692'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/optimizing-print-quality-of-pcb-artwork.html' title='Optimizing print quality of PCB artwork on transparency films'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-qc42ttjGuqQ/TuRU8M0drdI/AAAAAAAAA_0/5xsoZFH5Ykg/s72-c/joy-transparency-film-box.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4755030885658225251</id><published>2012-01-19T02:31:00.001+08:00</published><updated>2012-01-19T09:51:35.657+08:00</updated><title type='text'>Toy traffic light using a signal tower</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-QPbAnadphtY/TxbsVLI5ySI/AAAAAAAABDI/ONfsL8_uvPs/s1600/traffic-light-operating.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-QPbAnadphtY/TxbsVLI5ySI/AAAAAAAABDI/ONfsL8_uvPs/s200/traffic-light-operating.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;Just finished building a toy traffic light for the kids. It uses a signal tower (used on factory floors to provide visual alarms and indication of machine status) which employs 12VDC incandscent lamps. I haven't checked but the bulbs look like the same size and actually have same socket type as automobile tailight lamps. &lt;br /&gt;&lt;br /&gt;There's nothing particularly unique or special about this circuit, although I haven't come across any toy traffic light design that uses signal towers. What I do like about this circuit is the fact that a single user interface--a momentary contact pushbutton--controls all the functions, and that I didn't have to include any indicator LED or buzzer and instead could use the lamps of the traffic light itself to provide feedback to the user. &lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/Q1w6QuhAQQo" width="480"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Operation&lt;/u&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;There are three modes:&lt;br /&gt;1. Flashing amber (0.8sec on, 0.8sec off)&lt;br /&gt;2. Flashing red (0.8sec on, 0.8sec off)&lt;br /&gt;3. Normal traffic light operation: green --&amp;gt; amber --&amp;gt; red (variable red/green on-time, amber on-time = 2sec)&lt;br /&gt;&lt;br /&gt;Upon power up the traffic light starts in mode 1. Modes are selected by pressing the button momentarily. &lt;br /&gt;&lt;br /&gt;The stop (red) and go (green) time is user selectable. This is accomplished by pressing and holding the button down (regardless of what mode the traffic light is in). 1.5 seconds after pressing the button all lights will be turned off indicating the green light is about to begin showing the amount of go/stop time. Three seconds after pressing the button the green light will go through sets of blinking. The number of flashes per set indicates the go/stop time:&lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="5" cellspacing="1"&gt;&lt;tbody&gt;&lt;tr&gt; &lt;th&gt;Number of flashes per set&lt;/th&gt; &lt;th&gt;Go/Stop time in seconds&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;1&lt;/td&gt; &lt;td&gt;5&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;2&lt;/td&gt; &lt;td&gt;10&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;3&lt;/td&gt; &lt;td&gt;15&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;4&lt;/td&gt; &lt;td&gt;20&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;As with the modes keeping the button depressed will eternally cycle through the four sets. To choose a go/stop time the user releases the button &lt;i&gt;after&lt;/i&gt; the particular set of flashes has occured. So letting go of the button after the set of three flashes of the green light will set the go time and stop time to 15 seconds. The selected go/stop time is stored in EEPROM so the circuit doesn't forget even when powered down. Once the button is released the traffic light goes into mode 3 and begins with the yield (amber) light on.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Hardware&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-YxupkVDgbWc/Txbr6TuU_mI/AAAAAAAABC8/2Emavpn2rL8/s1600/trafflic-light-cont-schemat.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-YxupkVDgbWc/Txbr6TuU_mI/AAAAAAAABC8/2Emavpn2rL8/s400/trafflic-light-cont-schemat.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I originally planned to use the PIC12615 which is more than adequate. Unfortunately I need EEPROM which the 615 lacks. The only 8-pin PIC with EEPROM that I have right now is the 12F1822 and so that's what I ended up using for the MCU.&lt;br /&gt;&lt;br /&gt;The circuit has a polarity reversal protection in the form of a 1N5822 Scottky diode (D1 in the schematic) which has a rated capacity of 3A. Although the wall wart has a no-load measured output of 16VDC, this drops to around 12V when any of the lamps is on. Each of the power transistors also has a voltage drop across their collector-emitter. So to minimize further dip in voltage across the the incandescents I opted to use a Schottky instead of an ordinary rectifier. I've used the TIP102 NPN Darlington since as &lt;a href="http://electromotiveforces.blogspot.com/2012/01/npn-darlington-test.html"&gt;I reported in the Darlington transistor test&lt;/a&gt;, the 102 has the least collector-emitter voltage in the three types I was able to get hold of and play around with. As stated in that test using this very same signal tower, the power dissipation in the Darlingtons is such that heatsinks can be dispensed with.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-eWejFIQAJfg/TxbQ1k6oA0I/AAAAAAAABCw/k4CMgjXkUSo/s1600/traffic-light-cont-board-comp-side.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-eWejFIQAJfg/TxbQ1k6oA0I/AAAAAAAABCw/k4CMgjXkUSo/s200/traffic-light-cont-board-comp-side.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;Here's the finished board with all the components soldered. You'll notice there are several drill holes around the MCU. I've added pads to the unused MCU pins as well to the +5V rail and to the pushbutton tracks. These are "just in case" pads if ever I need to modify the board. The additional pushbutton pads are 0.1" apart and will accommodate a 2-way (2-pin) connector. I was going to solder the connector in case things don't work out and the board-mounted pushbutton doesn't work the way I planned. That rod sticking out of the blue pushbutton is a one and quarter inch piece of bamboo cut from a disposable chopstick. It extends out of case (through a small drilled hole) and will act as the button the user will pushing. It's the rod-poking-out-of-the-case part which I wasn't sure would work--and thus the backup connector for a case-mounted pushbutton. I applied a coat of nail polish to the chopstick just to seal the pores and prevent it from being attacked by molds (I've noticed that some brand new but long stored chopsticks have molds on them and I've seen molds appear on a bamboo chopping board after it had been left damp for some 24 hours).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-9Q72QeIunp4/TxbQ01Epo-I/AAAAAAAABCk/qB3m6jFx6_Y/s1600/traffic-light-cont-board-so.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-9Q72QeIunp4/TxbQ01Epo-I/AAAAAAAABCk/qB3m6jFx6_Y/s200/traffic-light-cont-board-so.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;I made the 12VDC traces to and from the incandescent lamps including ground really wide--150 mils--to minimize voltage drop. The traces narrow to just 50mils near the TO-220 pads to prevent shorts. Yes, the text at the bottom of the board is a dedication to the users of this circuit--my young nephews and niece. Naturally I chose Comic Sans as the font. You might notice scratches on the green photosensitive coating (which I always intentionally leave on the tracks). There was a lot of flux residue after soldering so I washed the board with detergent and scrubbed it using a toothbrush. So the blame for those unsightly scratches rests squarely on the shoulders of that brush! &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/--Ag2PRqu82w/TxbQ0HgdPkI/AAAAAAAABCY/yQ_YBjjObRo/s1600/traffic-light-case-open.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/--Ag2PRqu82w/TxbQ0HgdPkI/AAAAAAAABCY/yQ_YBjjObRo/s200/traffic-light-case-open.png" width="150" /&gt;&lt;/a&gt;&lt;/div&gt;Although the case has provisions--standoffs with holes in them--for screw mounting of boards, I couldn't find small enough screws that would fit. So I just dabbed copious amounts of hot melt glue on the standoffs and pressed the board onto them. Part of the glue oozed through the drilled holes of the board and formed nice hemispherical beads. The hefty black wall wart is at the bottom right. Its wire is too short so I added a meter more so the kids have leeway in placing the traffic light in their play area.&lt;br /&gt;&lt;br /&gt;One of my concerns is that the children will knock this thing over and break it. So I cut out a sponge in the shape of circle with a diameter one and a half times larger than the signal tower. I can just glue it on top of the tower. It'll definitely break any falls and save the polystyrene or acrylic lenses from cracking and breaking. But the sponge solution is ugly to state it mildly. So I might instead superglue something akin to an O-ring around the sides of top cap of the tower. Not as elastic and shock absorbing as the sponge but it's still better than nothing at all. Another is option would be a quarter inch PVC or rubber tubing.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Firmware&lt;/u&gt; &lt;br /&gt;&lt;br /&gt;I simply adapted the firmware I used for the &lt;a href="http://electromotiveforces.blogspot.com/2011/03/four-way-traffic-light.html"&gt;4-way traffic light&lt;/a&gt; I built the kids last year. This signal tower version is actually a lot simplier--no charlieplexing necessary. I opted to just clock the MCU using its default power-on frequency of 500kHz. Only one timer is used. I can no longer remember why I chose timer2 instead of timer0. For expediency's sake I didn't bother changing what ain't broken. A state machine is used with a timer tick of 4ms. In the configuration word, watchdog timer, power up timer, brownout reset, stack over/underflow reset are all enabled. All other options are disabled. Internal oscillator is used with I/O function on all pins.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/*&lt;br /&gt;&lt;br /&gt;Kids' Traffic Light Using a Signal Tower&lt;br /&gt;&lt;br /&gt;Created:        January 10 2012&lt;br /&gt;Processor:      PIC 12F1822&lt;br /&gt;Compiler        mikroC Pro 5.0.0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;* Uses a 12VDC wall wart as power supply&lt;br /&gt;&lt;br /&gt;* Tower light 12VDC red, amber, green incandescent lamps are switched by NPN Darlington transistors&lt;br /&gt;&lt;br /&gt;* pushbutton:&lt;br /&gt;     * when momentarily pressed: cycles through different possible modes:&lt;br /&gt;          1. normal traffic light: green -&amp;gt; amber -&amp;gt; red&lt;br /&gt;          2. flashing amber&lt;br /&gt;          3. flashing red&lt;br /&gt;     * when pressed and kept depressed for over a couple of seconds: cycles through different green/red light on time:&lt;br /&gt;          1. 5sec&lt;br /&gt;          2. 10sec&lt;br /&gt;          3. 15sec&lt;br /&gt;          4. 20sec.&lt;br /&gt;&lt;br /&gt;* green/red light on time is stored in EEPROM&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--------------------------+--------------------------------------&lt;br /&gt;No. of green flashes/set  |   Green/red light on time in seconds&lt;br /&gt;--------------------------+--------------------------------------&lt;br /&gt;         1                |            5&lt;br /&gt;         2                |           10&lt;br /&gt;         3                |           15&lt;br /&gt;         4                |           20&lt;br /&gt;--------------------------+--------------------------------------&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       input / output&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  lred      LATA.f0                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;#define  lamber    LATA.f1                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;#define  lgreen    LATA.f2                       // NPN darlington switches 12VDC incandescent lamp&lt;br /&gt;&lt;br /&gt;#define  tris_red  TRISA.f0&lt;br /&gt;#define  tris_amber TRISA.f1&lt;br /&gt;#define  tris_green TRISA.f2&lt;br /&gt;&lt;br /&gt;#define  pb        PORTA.f3                      // momentary contact push button&lt;br /&gt;#define  tris_pb   TRISA.f3&lt;br /&gt;&lt;br /&gt;#define  wpullup   0b111000                      // for WPUA -- enable pull ups on pb and unused pins&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       for traffic light&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  ambertime           500       // in normal mode -- amount of time for amber light to be on after green and before red -- time in terms of timer2 ticks&lt;br /&gt;#define  flashambertime      200       // in flashing amber mode -- amount of time for amber light to be on and amount of time to be off -- time in terms of timer2 ticks&lt;br /&gt;#define  flashredtime        200       // in flashing red mode -- amount of time for red light to be on and amount of time to be off -- time in terms of timer2 ticks&lt;br /&gt;#define  flashgreentime      50        // during selection of green/red light on time -- amount of time for green light to be on and amount of time to be off when it is flashing -- time in terms of timer2 ticks&lt;br /&gt;&lt;br /&gt;#define  changergtime        750       // minimum amount of time for button to be kept pressed before cycling through the green light on time -- time in terms of timer2 ticks&lt;br /&gt;                                       // given 4ms timer2 tick, changergtime of 250 = 1sec real time&lt;br /&gt;&lt;br /&gt;#define  prechangergtime     changergtime - 375  // amount of time after button is held down when all lights are turned off in preparation for possible change of green/red light on time&lt;br /&gt;&lt;br /&gt;#define  rgtime_increm       1250      // amount of time for green/red light to be on, and multiples thereof upon continual button press -- value in terms of timer2 ticks.&lt;br /&gt;#define  maxgtime            4         // maximum allowed multiple of rgtime_increm&lt;br /&gt;&lt;br /&gt;int16 REDGREENTIME;                    // amount of time for green/red light to be on -- time in terms of timer2 ticks&lt;br /&gt;int8  RGTIME;                          // multiples of rgtime_increm such that RGTIME*rgtime_increm = REDGREENTIME&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       eeprom&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  addr_rgtime      0x10         // eeprom address for user selected green/red light on time&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       for pushbutton&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;#define  rising              1         // rising edge detected. used by PBedge&lt;br /&gt;#define  released            1         // rising edge detected. used by PBedge&lt;br /&gt;#define  falling             2         // falling edge detected. used by PBedge&lt;br /&gt;#define  pressed             2         // falling edge detected. used by PBedge&lt;br /&gt;#define  none                0         // no edge. used by PBedge&lt;br /&gt;&lt;br /&gt;int8 PBval;                            // last eight values of the switch upon reading it&lt;br /&gt;int8 PBedge;                           // edge detected?, 0 = no edge detect, 1 = rising edge, 2 = falling edge; other values = Not Used / Undefined for now&lt;br /&gt;bit PBlevel;                           // voltage level of switch when not bouncing (hi = 1, lo = 0)&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       general defines and variables&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define  int1                bit&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;#define  int32               unsigned long&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;#define  _on                 0&lt;br /&gt;#define  _off                1&lt;br /&gt;&lt;br /&gt;#define  yes                 1&lt;br /&gt;#define  no                  0&lt;br /&gt;&lt;br /&gt;#define  input               1         // for TRISx&lt;br /&gt;#define  output              0         // for TRISx&lt;br /&gt;&lt;br /&gt;#define  analog              1         // for ANSELx&lt;br /&gt;#define  digital             0         // for ANSELx&lt;br /&gt;&lt;br /&gt;#define  hi                  1         // switch level high&lt;br /&gt;#define  lo                  0         // switch level low&lt;br /&gt;&lt;br /&gt;int16 TIME = 0;                        // records how long a light has been on  -- in terms of timer2 ticks&lt;br /&gt;&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;//       for state machines&lt;br /&gt;// ************************************************************************************************&lt;br /&gt;&lt;br /&gt;enum {_normal, _flashingamber, _flashingred, _flashgreen, _standby} STATEMODE = _flashingamber;&lt;br /&gt;enum {_go, _yield, _stop} STATENORMAL = _stop;&lt;br /&gt;enum {_init, _flash} STATEFLASHGREEN = _init;&lt;br /&gt;&lt;br /&gt;// ===========================================================================================&lt;br /&gt;//       functions&lt;br /&gt;// ===========================================================================================&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void ComputeRedGreenTime()&lt;br /&gt;{&lt;br /&gt;  REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IniReg()&lt;br /&gt;{&lt;br /&gt;  TRISA = input;&lt;br /&gt;  ANSELA = digital;&lt;br /&gt;  &lt;br /&gt;  tris_red = output;&lt;br /&gt;  tris_amber = output;&lt;br /&gt;  tris_green = output;&lt;br /&gt;&lt;br /&gt;  // enable weak pull up for pushbutton and unused pins&lt;br /&gt;  OPTION_REG.NOT_WPUEN = 0;&lt;br /&gt;  WPUA = wpullup;&lt;br /&gt;  &lt;br /&gt;  // default clock frequency is 500kHz upon power up.&lt;br /&gt;  // Timer2 is used for state machine and switch debouncing timing tick&lt;br /&gt;  // with clock = 500kHz, PR2 = 125, prescale = 1:4, postscale = 1:1&lt;br /&gt;  // TMR2 will count from zero to PR2 and timer2 interrupt occurs every 125*4 / (500kHz / 4) = 4ms = timer2 tick&lt;br /&gt;  T2CON = 0b101;             // postscaler = 1:1, prescaler = 1:4, timer2 on&lt;br /&gt;  TMR2 = 0;&lt;br /&gt;  PR2 = 125;&lt;br /&gt;&lt;br /&gt;  WDTCON = 0b1000;           // prescale = 1:512 (16ms typical)&lt;br /&gt;                             // WDTE in configuration word is configured so that WDT enabled when MCU awake and disabled when MCU asleep&lt;br /&gt;&lt;br /&gt;  // retrieve stored green/red light on time value.&lt;br /&gt;  // If eeprom-stored value is out of valid range then set it to minimum and store this value in eeprom&lt;br /&gt;  // stored values are in terms of multiples of rgtime_increm such that stored value multiplied by rgtime_increm = time in terms of TMR2 ticks&lt;br /&gt;  RGTIME = EEPROM_Read(addr_rgtime);&lt;br /&gt;  if (RGTIME == 0 || RGTIME &amp;gt; maxgtime)&lt;br /&gt;  {&lt;br /&gt;    RGTIME = 1;&lt;br /&gt;    EEPROM_Write(addr_rgtime, RGTIME);&lt;br /&gt;  }&lt;br /&gt;  ComputeRedGreenTime();                     //REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;&lt;br /&gt;  // initialize push button&lt;br /&gt;  PBval = 0xFF;&lt;br /&gt;  PBlevel = hi;&lt;br /&gt;} // void InitRegisters()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// normal traffic light operation: green --&amp;gt; amber --&amp;gt; red&lt;br /&gt;// red/green light on time is user selectable&lt;br /&gt;void Normal()&lt;br /&gt;{&lt;br /&gt;  switch (STATENORMAL)&lt;br /&gt;  {&lt;br /&gt;    case _go:&lt;br /&gt;      if (++TIME &amp;lt; REDGREENTIME)&lt;br /&gt;        lgreen = on;&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _yield;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        lgreen = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _yield:&lt;br /&gt;      if (++TIME &amp;lt; ambertime)&lt;br /&gt;        lamber = on;&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _stop;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        lamber = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _stop:&lt;br /&gt;      if (++TIME &amp;lt; REDGREENTIME)&lt;br /&gt;        lred = on;&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        STATENORMAL = _go;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        lred = off;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      TIME = 0;&lt;br /&gt;      STATENORMAL = _yield;&lt;br /&gt;  } // switch (STATENORMAL)&lt;br /&gt;} // void StateMachNormal()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// amber flashes on and off&lt;br /&gt;void FlashingAmber()&lt;br /&gt;{&lt;br /&gt;  static bit flag;&lt;br /&gt;&lt;br /&gt;  if (++TIME &amp;lt; flashambertime)&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      lamber = on;&lt;br /&gt;    else&lt;br /&gt;      lamber = off;&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      flag = 0;&lt;br /&gt;    else&lt;br /&gt;      flag = 1;&lt;br /&gt;    TIME = 0;&lt;br /&gt;  }&lt;br /&gt;} // void FlashingAmber()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// red flashes on and off&lt;br /&gt;void FlashingRed()&lt;br /&gt;{&lt;br /&gt;  static bit flag;&lt;br /&gt;&lt;br /&gt;  if (++TIME &amp;lt; flashredtime)&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      lred = on;&lt;br /&gt;    else&lt;br /&gt;      lred = off;&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    if (flag)&lt;br /&gt;      flag = 0;&lt;br /&gt;    else&lt;br /&gt;      flag = 1;&lt;br /&gt;    TIME = 0;&lt;br /&gt;  }&lt;br /&gt;} // void FlashingRed()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// when user selects red/green light on time the green light is flashed 1,2,3,or 4 times &lt;br /&gt;// to indicate 5, 10, 15, 20sec of on time&lt;br /&gt;void FlashGreen()&lt;br /&gt;{&lt;br /&gt;  static int8 i, CYCLES;&lt;br /&gt;  static bit flag;&lt;br /&gt;  &lt;br /&gt;  switch (STATEFLASHGREEN)&lt;br /&gt;  {&lt;br /&gt;    case _init:&lt;br /&gt;      i = 0;&lt;br /&gt;      flag = 1;&lt;br /&gt;      TIME = 0;&lt;br /&gt;      CYCLES = RGTIME*2-1;&lt;br /&gt;      STATEFLASHGREEN = _flash;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flash:&lt;br /&gt;      if (++TIME &amp;lt; flashgreentime)&lt;br /&gt;      {&lt;br /&gt;        if (flag)&lt;br /&gt;          lgreen = on;&lt;br /&gt;        else&lt;br /&gt;          lgreen = off;&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        if (flag)&lt;br /&gt;          flag = 0;&lt;br /&gt;        else&lt;br /&gt;          flag = 1;&lt;br /&gt;        TIME = 0;&lt;br /&gt;        if (++i &amp;gt;= CYCLES)&lt;br /&gt;        {&lt;br /&gt;          lgreen = off;&lt;br /&gt;          STATEMODE = _standby;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;  } // switch (STATEFLASHGREEN)&lt;br /&gt;} // void FlashGreen()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void StateMachMain()&lt;br /&gt;{&lt;br /&gt;  switch (STATEMODE)&lt;br /&gt;  {&lt;br /&gt;    case _normal:&lt;br /&gt;      Normal();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flashingamber:&lt;br /&gt;      FlashingAmber();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flashingred:&lt;br /&gt;      FlashingRed();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _flashgreen:        // green light is flashed to indicate to user the length of time green/red light are on during normal mode&lt;br /&gt;      FlashGreen();&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case _standby:           // do nothing mode where all lights are off. used when changing green on time&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      STATEMODE = _normal;&lt;br /&gt;  }&lt;br /&gt;} // void StateMachMain()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void AllLightsOff()&lt;br /&gt;{&lt;br /&gt;  lred = off;&lt;br /&gt;  lamber = off;&lt;br /&gt;  lgreen = off;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void DebounceSwitch()&lt;br /&gt;{&lt;br /&gt;  // shift all bits to the left&lt;br /&gt;  // if switch reading is hi then let pb_val bit 0 = 1&lt;br /&gt;  PBval &amp;lt;&amp;lt;= 1;&lt;br /&gt;  if (pb)&lt;br /&gt;    ++PBval;&lt;br /&gt;&lt;br /&gt;  PBedge = none;&lt;br /&gt;&lt;br /&gt;  // if level is lo and all bits of pb_val are now hi then&lt;br /&gt;  // a rising edge has been detected&lt;br /&gt;  // switch is considered just released when rising edge is detected&lt;br /&gt;  // switch level is now hi&lt;br /&gt; if ((!PBlevel) &amp;amp;&amp;amp; (PBval == 0xFF))&lt;br /&gt;  {&lt;br /&gt;    PBlevel = hi;&lt;br /&gt;    PBedge = rising;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // if level is hi and all bits of pb_val are now low then&lt;br /&gt;  // a falling edge has been detected&lt;br /&gt;  // switch is considered just pressed when falling edge is detected&lt;br /&gt;  // switch level is now lo&lt;br /&gt;  if ((PBlevel) &amp;amp;&amp;amp; (!PBval))&lt;br /&gt;  {&lt;br /&gt;    PBlevel = lo;&lt;br /&gt;    PBedge = falling;&lt;br /&gt;  }&lt;br /&gt;} // void DebounceSwitch()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void ProcessKey()&lt;br /&gt;{&lt;br /&gt;  int8 MODE;                           // temporary storage of current STATEMODE&lt;br /&gt;  &lt;br /&gt;  static  int16  PBPRESSTIMETOTAL = 0; // Keeps track of how long PB is depreesed in terms of timer2 ticks&lt;br /&gt;                                       // Keeps track of total time from falling edge (switched pressed) to rising edge (switch released).&lt;br /&gt;                                       // if less than changergtime then we know the keypress is momentary and is meant to cycle through the various traffic light modes&lt;br /&gt;&lt;br /&gt;  static  int16  PBPRESSTIME = 0;      // Keeps track of how long PB is depreesed in terms of timer2 ticks&lt;br /&gt;                                       // this variable is reset every time it exceeds changergtime&lt;br /&gt;                                       // every time it exceeds changergtime green on time is incremented&lt;br /&gt;&lt;br /&gt;  DebounceSwitch();&lt;br /&gt;  MODE = STATEMODE;&lt;br /&gt;  &lt;br /&gt;  if (PBlevel == lo)&lt;br /&gt;  {&lt;br /&gt;    if (++PBPRESSTIMETOTAL == prechangergtime)&lt;br /&gt;    {&lt;br /&gt;      AllLightsOff();&lt;br /&gt;      STATEMODE = _standby;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    if (++PBPRESSTIME &amp;gt;= changergtime)&lt;br /&gt;    {&lt;br /&gt;      PBPRESSTIME = 0;&lt;br /&gt;      if (++RGTIME &amp;gt; maxgtime)&lt;br /&gt;        RGTIME = 1;&lt;br /&gt;      EEPROM_Write(addr_rgtime, RGTIME);&lt;br /&gt;      ComputeRedGreenTime();                 //REDGREENTIME = RGTIME*rgtime_increm;&lt;br /&gt;      STATEMODE = _flashgreen;&lt;br /&gt;      STATEFLASHGREEN = _init;&lt;br /&gt;    } // if (++PBPRESSTIME &amp;gt;= changergtime)&lt;br /&gt;  } // if (PBlevel == lo)&lt;br /&gt;&lt;br /&gt;  if (PBedge == released)&lt;br /&gt;  {&lt;br /&gt;    if (PBPRESSTIMETOTAL &amp;lt; changergtime)&lt;br /&gt;    {&lt;br /&gt;      switch (MODE)&lt;br /&gt;      {&lt;br /&gt;        case _flashingred:&lt;br /&gt;          STATEMODE = _normal;&lt;br /&gt;          STATENORMAL = _yield;&lt;br /&gt;          break;&lt;br /&gt;          &lt;br /&gt;        case _normal:&lt;br /&gt;          STATEMODE = _flashingamber;&lt;br /&gt;          break;&lt;br /&gt;           &lt;br /&gt;        case _flashingamber:&lt;br /&gt;          STATEMODE = _flashingred;&lt;br /&gt;          break;&lt;br /&gt;          &lt;br /&gt;        default:&lt;br /&gt;          STATEMODE = _normal;&lt;br /&gt;      } // switch (MODE)&lt;br /&gt;    } // if (PBPRESSTIMETOTAL &amp;lt; changergtime)&lt;br /&gt;    else  // green/red light on time has been changed so go to normal traffic light mode and begin with amber light&lt;br /&gt;    {&lt;br /&gt;      STATEMODE = _normal;&lt;br /&gt;      STATENORMAL = _yield;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    AllLightsOff();&lt;br /&gt;    TIME = 0;                // reset timer so that whatever mode has been selected, light will go through full time allotted&lt;br /&gt;    PBPRESSTIMETOTAL = 0;&lt;br /&gt;    PBPRESSTIME = 0;&lt;br /&gt;  } // if (PBedge == released)&lt;br /&gt;} // void ProcessKey()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  IniReg();&lt;br /&gt;&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (PIR1.TMR2IF)&lt;br /&gt;    {&lt;br /&gt;      PIR1.TMR2IF = 0;&lt;br /&gt;      asm{clrwdt}&lt;br /&gt;      ProcessKey();&lt;br /&gt;      StateMachMain();&lt;br /&gt;    } // if (PIR1.TMR2IF)&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;10am Addendum: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-MswLNlQCuz4/Txd1N_gD59I/AAAAAAAABDU/sFXr_BXTBMc/s1600/traffic-light-bumper.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-MswLNlQCuz4/Txd1N_gD59I/AAAAAAAABDU/sFXr_BXTBMc/s200/traffic-light-bumper.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;Rummaged through the junk bins and found this silicone gasket. It's probably from some old thermos bottle. Still very elastic, has an internal diameter several millimeters smaller than the signal tower's and even matches the cream color of the signal tower and circuit board's plastic case. Great find. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ot9RkTixQBw/Txd1ZrTWf-I/AAAAAAAABDg/thF3RsR8JlY/s1600/traffic-light-bumper-instal.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-ot9RkTixQBw/Txd1ZrTWf-I/AAAAAAAABDg/thF3RsR8JlY/s200/traffic-light-bumper-instal.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;I wiped down the sides of the cap with alcohol, let it dry, coated it with a thin layer of cyanoacrylate adhesive, and then slipped the gasket on. The only problem is I didn't expect the superglue to set within 10 seconds (usually takes about half a minute) so part of what is now the rubber bumper isn't properly aligned with the cap. Oh well.&lt;br /&gt;&lt;br /&gt;I knocked the tower over a couple of times. From the sound of the thud it still doesn't match the exceptional shock absorbing properties of the sponge but this will have to do. It certainly beats having no bumpers at all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4755030885658225251?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4755030885658225251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/toy-traffic-light-using-signal-tower.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4755030885658225251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4755030885658225251'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/toy-traffic-light-using-signal-tower.html' title='Toy traffic light using a signal tower'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-QPbAnadphtY/TxbsVLI5ySI/AAAAAAAABDI/ONfsL8_uvPs/s72-c/traffic-light-operating.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4554679664293147155</id><published>2012-01-14T11:36:00.000+08:00</published><updated>2012-01-14T11:36:34.700+08:00</updated><title type='text'>NPN Darlington test</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.netmotion.com/htm_files/ot_patlite.htm" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://www.netmotion.com/graphics/oth/pat_flashing.gif" width="83" /&gt;&lt;/a&gt;&lt;/div&gt;Am currently modding a signal tower I bought the nephews months ago and converting it into a multi-featured traffic light for them to play with. The tower has 12VDC red, amber and green incandescent lamps. Back when I bought it I just wired individual rocker switches for each of the lamps and powered it via a 12VDC wall cube. It was a fully manual system where any combination of lights could be turned on.&lt;br /&gt;&lt;br /&gt;I've removed all the switches and will be adding a brain so it can function automatically. Initial measurements showed the incandescents draw about half an ampere each. To switch them I'd need either relays or power transistors. I've opted to use NPN Darlingtons instead of relays even if Darlingtons will have a substantial voltage drop across the collector-emitter. A more major concern, however, is the power dissipation because of a Darlington's high voltage drop. Dissipation translates to heat which means high temperatures which, if high enough, would require the use of heatsinks--something I would rather not have to resort to. So in order to find out whether I'd be better off using miniature PCB mounted relays, I bought a few Darlingtons and tested their V&lt;sub&gt;CE&lt;/sub&gt; and the temperature of their cases.&lt;br /&gt;&lt;br /&gt;The corner store from which I get my parts doesn't have a good range of Darlingtons and so the parts I ended up testing were the &lt;a href="http://www.fairchildsemi.com/ds/TI/TIP102.pdf"&gt;TIP102&lt;/a&gt;, &lt;a href="http://www.fairchildsemi.com/ds/TI/TIP112.pdf"&gt;TIP112&lt;/a&gt;, and &lt;a href="http://www.fairchildsemi.com/ds/TI/TIP120.pdf"&gt;TIP120&lt;/a&gt;. I breadboarded the following circuit and powered it off &lt;a href="http://electromotiveforces.blogspot.com/2009/12/atx-in-ats-clothing.html"&gt;my bench supply&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/--sslH7MD5Rg/TxDX7oUamHI/AAAAAAAABCI/9-i8soPc5vI/s1600/darlington.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/--sslH7MD5Rg/TxDX7oUamHI/AAAAAAAABCI/9-i8soPc5vI/s320/darlington.png" width="288" /&gt;&lt;/a&gt;&lt;/div&gt;Resistor R5 represents the incandescent lamp. The following tables show the measurements I obtained for each of the bulbs and transistors. V&lt;sub&gt;CE&lt;/sub&gt; is the voltage across the collector and emitter. I&lt;sub&gt;C&lt;/sub&gt; is the current going the lamp. Case temperature was measured using a Fluke 62 infrared thermometer around two inches away from the Darlington. I recorded the maximum reading. V&lt;sub&gt;CE&lt;/sub&gt; drifted as the transistor temperature went up so I gave the circuit a settling time of between two to three minutes before taking the measurements. I previously took some measurements of the inrush current (without using the Darlingtons) using a Fluke 87V's Peak-Min-Max function and the highest value I obtained was 5.2A.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="5" cellspacing="0"&gt;&lt;caption&gt;TIP120 NPN DARLINGTON&lt;/caption&gt;  &lt;tbody&gt;&lt;tr&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;Red&lt;/th&gt; &lt;th&gt;Amber&lt;/th&gt; &lt;th&gt;Green&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;V&lt;sub&gt;CE&lt;/sub&gt; (volts)&lt;/td&gt; &lt;td&gt;0.735&lt;/td&gt; &lt;td&gt;0.744&lt;/td&gt; &lt;td&gt;0.745&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;I&lt;sub&gt;C&lt;/sub&gt; (amperes)&lt;/td&gt; &lt;td&gt;0.363&lt;/td&gt; &lt;td&gt;0.467&lt;/td&gt; &lt;td&gt;0.469&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Case Temp. (Celsius)&lt;/td&gt; &lt;td&gt;41&lt;/td&gt; &lt;td&gt;43&lt;/td&gt; &lt;td&gt;43&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;table border="1" cellpadding="5" cellspacing="0"&gt;&lt;caption&gt;TIP112 NPN DARLINGTON&lt;/caption&gt;  &lt;tbody&gt;&lt;tr&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;Red&lt;/th&gt; &lt;th&gt;Amber&lt;/th&gt; &lt;th&gt;Green&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;V&lt;sub&gt;CE&lt;/sub&gt; (volts)&lt;/td&gt; &lt;td&gt;0.714&lt;/td&gt; &lt;td&gt;0.728&lt;/td&gt; &lt;td&gt;0.730&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;I&lt;sub&gt;C&lt;/sub&gt; (amperes)&lt;/td&gt; &lt;td&gt;0.360&lt;/td&gt; &lt;td&gt;0.467&lt;/td&gt; &lt;td&gt;0.469&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Case Temp. (Celsius)&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;td&gt;43&lt;/td&gt; &lt;td&gt;43&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;table border="1" cellpadding="5" cellspacing="0"&gt;&lt;caption&gt;TIP102 NPN DARLINGTON&lt;/caption&gt;  &lt;tbody&gt;&lt;tr&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;Red&lt;/th&gt; &lt;th&gt;Amber&lt;/th&gt; &lt;th&gt;Green&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;V&lt;sub&gt;CE&lt;/sub&gt; (volts)&lt;/td&gt; &lt;td&gt;0.691&lt;/td&gt; &lt;td&gt;0.700&lt;/td&gt; &lt;td&gt;0.697&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;I&lt;sub&gt;C&lt;/sub&gt; (amperes)&lt;/td&gt; &lt;td&gt;0.362&lt;/td&gt; &lt;td&gt;0.468&lt;/td&gt; &lt;td&gt;0.470&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Case Temp. (Celsius)&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;td&gt;41&lt;/td&gt; &lt;td&gt;42&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;The TIP102 clearly stands out as having the least V&lt;sub&gt;CE&lt;/sub&gt; and consequently the lowest power dissipation. The temperature as measured by the IR thermometer is of course hardly accurate (I could've used the 87V's thermocouple but I wasn't in the mood to go through the hassle). Since we have all the values necessary let's compute for the (near actual) case temperature. The TO-220 package has a junction to ambient thermal resistance of 62.5°C/W. Current draw of the green bulb is 0.47A. V&lt;sub&gt;CE&lt;/sub&gt; = 0.697V. Power dissipation is therefore 0.697 x 0.47 = 0.328W. Temperature rise of the case is therefore 62.5 x 0.328 = 20.5°C. Ambient temperature was measured to be 30°C. So case temp. ought to be approximately 30 + 20.5 = 50.5°C. &lt;br /&gt;&lt;br /&gt;The TIP102 is the most expensive amongst the three Darlingtons, but by less than 10% over the cheapest. Given how I'm averse to designing in heatsinks for this toy I'll use the TIP102, despite the fact that its temperature will be a mere 1.4° less than if I use the TIP120.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4554679664293147155?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4554679664293147155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/npn-darlington-test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4554679664293147155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4554679664293147155'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2012/01/npn-darlington-test.html' title='NPN Darlington test'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/--sslH7MD5Rg/TxDX7oUamHI/AAAAAAAABCI/9-i8soPc5vI/s72-c/darlington.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-2940460475957870633</id><published>2011-12-31T21:53:00.003+08:00</published><updated>2012-01-01T23:19:17.275+08:00</updated><title type='text'>MCP79400 RTCC breakout board</title><content type='html'>Last design for the year! 32.768kHz crystal and capacitors will be onboard as well as the Schottky diode and current limiting resistor for the back-up coin battery (which will be off-board). PCB measures 0.8 x 1.0 inch. All components except for the 0.1" male headers are on the copper side (am using a single-sided presensitized board) because the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en551550"&gt;MCP79400&lt;/a&gt; real time clock calendar chip is available only in surface mount packages. I don't have any chip resistors and capacitors so the discretes are all through-holes. The two headers are spaced 0.7-inch apart so they'll still plug nicely into a breadboard.  &lt;br /&gt;&lt;br /&gt;I could have this board fabricated tomorrow. Thing is, I have yet to purchase my very first MCP79400! Might do so in mid-January. &lt;br /&gt;&lt;br /&gt;Ignore that yellow airwire at pin 6. I think it's a bug in DesignSpark PCB 3.0. Even after routing, some airwires remain. Even after executing a design rule check. And even after closing DesignSpark and opening it again. Go figure. &lt;br /&gt;&lt;br /&gt;You'll probably notice the SMD pads are much longer than recommended by chip manufacturers. When the chip is mounted on the board there should be around 2mm of exposed pad. The rationale is that the excess pad area permits the soldering iron tip to simultaneously make contact with the pad and the leads. Without the excess we'd have to position the iron tip on top of the pins to solder them. This translates to higher thermal resistance and longer heating time--not good for the chip.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Yk522wi2yM4/Tv8S_X45MfI/AAAAAAAABBk/Jnd6r5-TF1A/s1600/MCP79400%2Bbreakout%2Bboard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="253" src="http://4.bp.blogspot.com/-Yk522wi2yM4/Tv8S_X45MfI/AAAAAAAABBk/Jnd6r5-TF1A/s320/MCP79400%2Bbreakout%2Bboard.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7gi37wm1bOs/Tv8S_fY-3AI/AAAAAAAABBY/z3g9WOyWkCI/s1600/MCP79400%2Bbreakout%2Bboard%2Bsilkscreen%2Bonly.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="254" src="http://1.bp.blogspot.com/-7gi37wm1bOs/Tv8S_fY-3AI/AAAAAAAABBY/z3g9WOyWkCI/s320/MCP79400%2Bbreakout%2Bboard%2Bsilkscreen%2Bonly.png" width="320" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;-----&lt;br /&gt;&lt;br /&gt;January 1, 2012 Addendum&lt;br /&gt;&lt;br /&gt;Made a couple of revisions. Added a 1uF filter cap just to make sure there's sufficient power on board. Also added a 10K pull resistor to the MFP pin as specified in the MCP79400 datasheet. The I2C's SDA and SCL lines need pull-ups but since their value depends on the I2C frequency I've left them out. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-c_ceg0upXnE/TwB4MXs381I/AAAAAAAABCA/mgndHzoOjxo/s1600/MCP79400%2Bbreakout%2Bboard%2Bschematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="170" src="http://3.bp.blogspot.com/-c_ceg0upXnE/TwB4MXs381I/AAAAAAAABCA/mgndHzoOjxo/s320/MCP79400%2Bbreakout%2Bboard%2Bschematic.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Ujeq5ukk6fg/TwB4MC6o_sI/AAAAAAAABBw/6d8Xp35xO30/s1600/MCP79400%2Bbreakout%2Bboard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="262" src="http://3.bp.blogspot.com/-Ujeq5ukk6fg/TwB4MC6o_sI/AAAAAAAABBw/6d8Xp35xO30/s320/MCP79400%2Bbreakout%2Bboard.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-2940460475957870633?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/2940460475957870633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/mcp79400-rtcc-breakout-board.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2940460475957870633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2940460475957870633'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/mcp79400-rtcc-breakout-board.html' title='MCP79400 RTCC breakout board'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-Yk522wi2yM4/Tv8S_X45MfI/AAAAAAAABBk/Jnd6r5-TF1A/s72-c/MCP79400%2Bbreakout%2Bboard.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-3369358241364675051</id><published>2011-12-30T23:50:00.000+08:00</published><updated>2011-12-30T23:50:21.680+08:00</updated><title type='text'>Voltmeter woes</title><content type='html'>Just got duped. The digital panel-mounting voltmeter I bought which supposedly has a range of 0 to 20VDC stops measuring--with fairly good accuracy--at a little over 15V. Feed any voltage higher than that and meter's accuracy starts dropping off fast. By the time the measured voltage is 20V the meter reads less than 17V. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-NxukyjHOiXE/Tv3anMxhXBI/AAAAAAAABBA/CHUiyyygiRU/s1600/voltmeter-reading-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-NxukyjHOiXE/Tv3anMxhXBI/AAAAAAAABBA/CHUiyyygiRU/s320/voltmeter-reading-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-sqcePd_I-6o/Tv3amtSsBKI/AAAAAAAABA0/ooKdiIkG1mo/s1600/voltmeter-reading-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-sqcePd_I-6o/Tv3amtSsBKI/AAAAAAAABA0/ooKdiIkG1mo/s320/voltmeter-reading-2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-OljMBzMRWu4/Tv3amI8xPmI/AAAAAAAABAo/uZzYaf0kKUU/s1600/voltmeter-reading-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-OljMBzMRWu4/Tv3amI8xPmI/AAAAAAAABAo/uZzYaf0kKUU/s320/voltmeter-reading-3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The exasperating part about it is I found this out only after I had redesigned a circuit so as to factor its limited range of 20V. If you haven't guessed it yet, the cheapo meter is made in China. Yes, it came in that blue and white box with Chinese characters. Doesn't come with any instruction leaflet. And there are only Chinese text on all sides of the carton. Frustrating. At least the silkscreen on the board isn't in Chinese! As you can probably make out, the pins for the connectors are marked (left to right) 5VIN, GND, GND, VIN+. The first two pins are for power and the latter two are for the measured voltage input. A check using a DMM shows the two middle pins (GND) are in fact connected to one another on the PCB. The multiturn potentiometer (blue rectangular component at the top) is used to calibrate the meter. Calibration is fine--if you don't need the entire specified range of 20V. Else, it's a joke.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-KzSjEXlzpA4/Tv3b2_NFdCI/AAAAAAAABBM/4TqUM4FKEz0/s1600/voltmeter-pcb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="199" src="http://4.bp.blogspot.com/-KzSjEXlzpA4/Tv3b2_NFdCI/AAAAAAAABBM/4TqUM4FKEz0/s320/voltmeter-pcb.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-3369358241364675051?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/3369358241364675051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/voltmeter-woes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3369358241364675051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3369358241364675051'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/voltmeter-woes.html' title='Voltmeter woes'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-NxukyjHOiXE/Tv3anMxhXBI/AAAAAAAABBA/CHUiyyygiRU/s72-c/voltmeter-reading-1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7470697064808523616</id><published>2011-12-13T21:08:00.000+08:00</published><updated>2011-12-13T21:08:33.770+08:00</updated><title type='text'>Devil's in the details</title><content type='html'>And I just got burned. I inadvertently designed a short circuit in a pcb! &lt;br /&gt;&lt;br /&gt;I create circuit board artwork using &lt;a href="http://www.designspark.com/pcb"&gt;DesignSpark PCB&lt;/a&gt; and when printing it out on transparency film I always have the board outline printed out as well. The outline allows for precise alignment of the mask (the film) and the presensitized board prior to exposure. In this particular design of a simple 5-volt power supply I had a wide strip of copper for both the +5V and ground. &lt;i&gt;And&lt;/i&gt; I extended those tracks all the way to the edge of the board (see the left side of the artwork image below). &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-509O6tD0uuI/TudJ6GnjvlI/AAAAAAAABAY/V_nSoiIRrsk/s1600/short%2Bcircuit%2Bon%2Bpcb%2Bartwork.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="170" src="http://1.bp.blogspot.com/-509O6tD0uuI/TudJ6GnjvlI/AAAAAAAABAY/V_nSoiIRrsk/s320/short%2Bcircuit%2Bon%2Bpcb%2Bartwork.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Yep, disaster in the waiting. I printed the artwork, exposed the presensitized board, etched it, drilled the holes, soldered the components, and all the while I was absolutely oblivious to the short I had created. I powered up the board while probing the output with a multimeter. Reading? 23mV. I cut off power immediately. Tried it again. Still the same zero voltage. I was so clueless of the booboo that I even desoldered one of the filter caps suspecting it might be defective. Only while poring through board with a magnifying lens did I finally spot the fine 5-mil trace at the very edge of the board bridging the power rails. Facepalm moment indeed. Actually if I had moved the mask by some half millimeter that outline would've been off the physical board and the short wouldn't have appeared.&lt;br /&gt;&lt;br /&gt;It's worth noting that even a 5-mil track is faithfully reproduced--implying there's insufficient undercutting during etching to remove the track. This is good of course--if we want such fine copper traces. Luckily it's easy to cut and gouge out the offending copper track. After making sure the bridge had been completely obliterated I soldered the cap back on and the circuit worked as designed. No electronic component was harmed in the making of this sophomoric design mistake. &lt;br /&gt;&lt;br /&gt;Hopefully the stress, anxiety, and panic I was subjected to has burned the lesson in my head--since I'll still continue the practice of printing the board outline, I must remember to leave some 20 mils around the perimeter of the board copper-free. I better burn that minimum gap requirement into DesignSpark's design rules. And more importantly, I better start paying attention to the error messages which the design rule check routine spits out--because I don't usually take a look at the track-to-track, pad-to-track, shape-to-track, shape-to-pad, etc. spacing errors. I have a knack for breaking rules. Unfortunately, this time around I got bitten.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-7470697064808523616?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/7470697064808523616/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/devils-in-details.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7470697064808523616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7470697064808523616'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/devils-in-details.html' title='Devil&apos;s in the details'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-509O6tD0uuI/TudJ6GnjvlI/AAAAAAAABAY/V_nSoiIRrsk/s72-c/short%2Bcircuit%2Bon%2Bpcb%2Bartwork.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7916430329320652745</id><published>2011-12-13T18:26:00.003+08:00</published><updated>2011-12-13T20:10:50.724+08:00</updated><title type='text'>Ambient light sensor on the roof</title><content type='html'>Installed an ambient light level sensor on the roof. Voltage from the simple voltage divider made up of an LDR and a fixed 5% resistor (750Kohm in this case) is digitized by the analog-to-digital converter of the PIC12F1822 (16 readings are taken successively--takes around 1ms--and averaged) and is sent out through the its serial port at 19.2kbps every 50ms. Communications is simplex--this transmitter merely sends while receivers in remote locations can only receive data. Serial data from the MCU is fed to the SN75176B transceiver which then converts the data stream to an RS-485 compliant signal which is then sent down a pair of unshielded twisted pair cable, for use by any circuit requiring the data. Checksum is sent with every data packet for data integrity verification.&lt;br /&gt;&lt;br /&gt;A pair of wires in the UTP cable provides filtered (but unregulated) power to the board. Because the input voltage is somewhere around 8 to 8.5V and may drop below 8 if loads elsewhere (various circuits use the same power supply) increase, I used a 1N5817 Schottky diode instead of a 1N400x for polarity reversal protection. That halves the forward voltage drop across the diode to just 0.3V and thus increases the headroom for the 78L05, ensuring it's able to maintain voltage regulation. There are separate power and ground tracks/traces for the analog and  digital sections to minimize digital noise from contaminating the sensor  section. The analog section has a 1uF tantalum filter cap and the MCP6232 op amp has the mandatory 0.1uF decoupling cap. You might notice the 1/4-watt resistor adjacent to the LDR. Reason is the dang supplier doesn't have the complete range of values for 1/8-watt resistors.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-OwG3rTHSB1M/Tuciwb3FA0I/AAAAAAAABAA/U3wXSHhsbdY/s1600/light-sensor-roof-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-OwG3rTHSB1M/Tuciwb3FA0I/AAAAAAAABAA/U3wXSHhsbdY/s320/light-sensor-roof-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Image below shows the board &lt;i&gt;in situ&lt;/i&gt; with the translucent plastic cover yet to be snapped on. Screws on the board act as standoffs to keep the soldered side from contact with the bottom which might accumulate moisture (hence the drilled holes on the blue plastic to act as drain). The gray 4-pair UTP cable can be seen on the lower left.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-B4DBQrSzplc/TuciwzgXWkI/AAAAAAAABAM/sE5qIMlJr5I/s1600/light-sensor-roof-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-B4DBQrSzplc/TuciwzgXWkI/AAAAAAAABAM/sE5qIMlJr5I/s320/light-sensor-roof-2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-7916430329320652745?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/7916430329320652745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/ambient-light-sensor-on-roof.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7916430329320652745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7916430329320652745'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/ambient-light-sensor-on-roof.html' title='Ambient light sensor on the roof'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-OwG3rTHSB1M/Tuciwb3FA0I/AAAAAAAABAA/U3wXSHhsbdY/s72-c/light-sensor-roof-1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-5083195340792871574</id><published>2011-12-04T18:42:00.000+08:00</published><updated>2011-12-04T18:42:41.087+08:00</updated><title type='text'>Grew up on Casio</title><content type='html'>&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-jYBvi5UEpJs/TttH8CxwV7I/AAAAAAAAA-U/gVyxEpFZDTo/s320/casio-fx3000-fx115ms.png" width="320" /&gt;&lt;/div&gt;&lt;br /&gt;Photo of Casio fx-3000 and fx-115MS scientific calculator. An uncle bought me the 3000 back in 1977. Was already gaga over sci calcs back then. It still works but I haven't used it in decades. Though not noticeable in the pic with the 115 (because we're looking head-on through yellow plastic filter) the LCD is already showing its age and deteriorating. Actually it's fared better than my &lt;a href="http://www.voidware.com/calcs/fx502p.htm"&gt;Casio fx-502P&lt;/a&gt; whose LCD went kaput years ago. The 3000's power switch had been giving me problems and had to take out its board and clean both the switch and board contacts.&lt;br /&gt;&lt;br /&gt;I now use the 6-year old 115 exclusively. It probably has twice the number of functions. The engineering units (nano, micro, kilo, etc) and decimal/binary/hex functions make life easier. And being able to go back and edit the formula/equation and plug in different values for the variables is just indispensable.&lt;br /&gt;&lt;br /&gt;As for speed, the two are truly generations apart. A "u" appears as the leftmost character in the 3000 to alert the user that the microcontroller is still running the numbers through its ALU (arithmetic logic unit). Even something as simple as 1+1 takes a quarter of a second. It's that slow. 69! takes 3.3sec. The 115 on the other hand spits out the factorial result in the blink of an eye, literally. Given that both calcs cannot handle or display any number greater than or equal to 10^100, 69 is the highest possible number for factorials for both.&lt;br /&gt;&lt;br /&gt;The equation shown on the display of the 115 is (2π*1k*1µ)&lt;sup&gt;-1&lt;/sup&gt;. This of course is the  formula for the cutoff frequency of a simple low pass filter with a 1kohm resistor and 1µF cap. Being able to enter "M" for mega, "k" for kilo, "n" for nano, "p" for pico, etc.comes in very handy. They  save me the trouble of keying in values in scientific notation. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-jKn0Y8PmO1w/TttIGF03H1I/AAAAAAAAA-g/X9T9tup5Qm8/s1600/casio-fx3000-front.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-jKn0Y8PmO1w/TttIGF03H1I/AAAAAAAAA-g/X9T9tup5Qm8/s320/casio-fx3000-front.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Back side of the calculator. The sticker is original. Could be the serial number of the unit. You  might be able to make out the printing at the bottom. Part of it says  "Made in Japan." All Casios were back then. The 115MS on the other hand is churned out in  China. Thumbs down.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ahU12DSrR48/TttIOXm5cEI/AAAAAAAAA-s/JxE0onOZoDg/s1600/casio-fx3000-back.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-ahU12DSrR48/TttIOXm5cEI/AAAAAAAAA-s/JxE0onOZoDg/s320/casio-fx3000-back.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Without the front panel you can clearly see the poor state of the LCD.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-zzio-eMJwoM/TttIW5n28JI/AAAAAAAAA-4/fhuB2cstnEc/s1600/casio-fx3000-board1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-zzio-eMJwoM/TttIW5n28JI/AAAAAAAAA-4/fhuB2cstnEc/s320/casio-fx3000-board1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The microcontroller is a NEC D895G. Googled but can't find any datasheet for it. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-2s1gh_6YhYg/TttJqk9UATI/AAAAAAAAA_Q/anQq1AyEZw0/s1600/casio-fx3000-board1a.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-2s1gh_6YhYg/TttJqk9UATI/AAAAAAAAA_Q/anQq1AyEZw0/s320/casio-fx3000-board1a.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;This is the side of the board that greets you when you take off the back cover.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/--hteFByaORw/TttIgofNDvI/AAAAAAAAA_E/Qs07R1Il5fc/s1600/casio-fx3000-board-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/--hteFByaORw/TttIgofNDvI/AAAAAAAAA_E/Qs07R1Il5fc/s320/casio-fx3000-board-2.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-5083195340792871574?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/5083195340792871574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/grew-up-on-casio.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5083195340792871574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5083195340792871574'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/grew-up-on-casio.html' title='Grew up on Casio'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-jYBvi5UEpJs/TttH8CxwV7I/AAAAAAAAA-U/gVyxEpFZDTo/s72-c/casio-fx3000-fx115ms.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-1007485073558909971</id><published>2011-12-04T14:40:00.000+08:00</published><updated>2011-12-04T14:40:52.279+08:00</updated><title type='text'>Rail-to-rail op amps</title><content type='html'>Just got bitten. Or rather, just found out after over two years I'd made a booboo. I had used a Microchip &lt;a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1335&amp;amp;dDocName=en010458"&gt;MCP618&lt;/a&gt; as a unity gain buffer for an &lt;a href="http://electromotiveforces.blogspot.com/2011/07/microcontroller-based-automatic-night.html"&gt;ANLC&lt;/a&gt; with the sensor unit located a remote location. I was upgrading the load-control part of the circuit yesterday and while monitoring the output of the op amp at between sunset and dusk I was shocked to see the voltage jump from from V&lt;sub&gt;DD&lt;/sub&gt; to around 4.80V. (Thereafter it decreased in a smooth continuous gradient). That sent me into a near-panic. Questions raced through my head. Was the long length of cable--some 20 meters--from sensor to control circuit responsible for this behavior? Do I need to add a high value load resistor? Is there some soldering/board defect at the sensor? &lt;br /&gt;&lt;br /&gt;Eventually I zeroed in on the fact that the 618 is not a full rail-to-rail op amp. Rechecking the datasheet and refreshing my memory, indeed while its output can, its input does not swing rail-to-rail. So to test whether the 618 is cause of the problem I bench-tested it using a 100K pot as input to the unity gain buffer. Sure enough as I ever so gradually turned the pot down from maximum, the buffer output jumps from 5.18 (V&lt;sub&gt;DD&lt;/sub&gt;) to around 4.90V.I tried this several times just to make sure its inherent. Well, it doesn't miss a beat. The same discontinuous output occurs every time.&lt;br /&gt;&lt;br /&gt;I then popped the 618 off the breadboard and dropped in a pin-for-pin compatible &lt;a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1335&amp;amp;dDocName=en010474"&gt;MCP6273&lt;/a&gt;. This op amp has true rail-to-rail input and output. As I was hoping, as the pot is wound down the output voltage does not exhibit the sudden drop but instead smoothly and continuously decreases from V&lt;sub&gt;DD&lt;/sub&gt;.&lt;br /&gt;&lt;br /&gt;Whew! I'm just glad it isn't some much more major error or defect. &lt;br /&gt;&lt;br /&gt;Moral of the story: Make sure the op amp has rail-to-rail input &lt;i&gt;and&lt;/i&gt; output if the application requires the entire range of values.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-1007485073558909971?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/1007485073558909971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/rail-to-rail-op-amps.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1007485073558909971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1007485073558909971'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/12/rail-to-rail-op-amps.html' title='Rail-to-rail op amps'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-2266086320586519737</id><published>2011-11-29T13:45:00.000+08:00</published><updated>2011-11-29T13:45:36.670+08:00</updated><title type='text'>Relay board</title><content type='html'>Over the years I've moved away from relays and adopted triacs for switching high voltage AC loads. Their quiet operation and compact size (heat sink excluded) are a plus. In the last few years, however, I've reverted to the electromechanical device. The prodigal son has returned (nahhh). Relays have their drawbacks including mechanical failure, contact point burning/pitting, and coil burnout, but they provide superb electrical isolation between control and power side of the circuit and negligible voltage drop across the contacts.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-10LZ60oJ0tc/TtQ4LDpc39I/AAAAAAAAA9w/iKKcVEaSauY/s1600/relay-board.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-10LZ60oJ0tc/TtQ4LDpc39I/AAAAAAAAA9w/iKKcVEaSauY/s320/relay-board.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;So here's my relay module v1.0 complete with polarity reversal protection diode, flyback diode, and LED power-on indicator. The relay in this case is a SPDT with a contact rating of 10A @240VAC/24VDC and a coil of 12VDC (measured resistance = 420 ohms). The board is designed such that the LED and its current limiting resistor need not be installed for the circuit to work--the additional current draw may be undesirable or an indicator lamp may simply be superfluous. Even the input diode in series with the positive rail isn't absolutely essential (if polarity reversal isn't an issue) and a jumper wire can be soldered in its stead. Omitting the flyback diode, on the other hand, is an experiment for foolhardy to try out.&lt;br /&gt;&lt;br /&gt;DC coil power input is via the 2-pin connector with red going to V+ as per convention, while relay contacts are accessed via the 3-way terminal block. I'm obsessed with isolation so I've placed them on opposite ends of the board.&lt;br /&gt;&lt;br /&gt;The paper phenolic board is 1 x 3 inches, scored and snapped off from a 4x3-inch "mother" board which contains three other clones. Yeah, I'm mass producing this.... Uhhh sort of.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-n_PgBzdgYpA/TtRwDiNxxsI/AAAAAAAAA-I/qOxCL9vY7rs/s1600/relay%2Bboard%2Bschematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://2.bp.blogspot.com/-n_PgBzdgYpA/TtRwDiNxxsI/AAAAAAAAA-I/qOxCL9vY7rs/s320/relay%2Bboard%2Bschematic.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-2266086320586519737?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/2266086320586519737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/relay-board.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2266086320586519737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2266086320586519737'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/relay-board.html' title='Relay board'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-10LZ60oJ0tc/TtQ4LDpc39I/AAAAAAAAA9w/iKKcVEaSauY/s72-c/relay-board.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-1802298043863870611</id><published>2011-11-26T21:56:00.000+08:00</published><updated>2011-11-26T21:56:40.033+08:00</updated><title type='text'>Random number generator</title><content type='html'>Needed a way to make a microcontroller turn a load on at random. A little research and I found an algorithm that goes by the highfalutin name &lt;a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register"&gt;&lt;i&gt;linear feedback shift register&lt;/i&gt;&lt;/a&gt;. Basically the bits in the register are shifted one bit either to the left or right and the result is then XORed with a constant. The content of the register changes in a pseudo random way. It isn't at all random because when we perform the shift and XOR the register goes through each possible value once and only once (each of 255 numbers for an 8-bit register) until we cycle back to the original seed value (initial value of the register). Thereafter, the exact same sequence gets repeated. &lt;br /&gt;&lt;br /&gt;It's important to make sure the seed stored in the register is nonzero, else it will remain zero. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.piclist.com/techref/microchip/rand8bit.htm"&gt;PIClist&lt;/a&gt; has a number of code snippets for implementing the LFSR in Microchip PICs. The following is for generating a 16-bit random number written in PIC18 assembly: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;  BCF     STATUS,C&lt;br /&gt;  RRCF    LFSRVALUEH,F&lt;br /&gt;  RRCF    LFSRVALUEL,F&lt;br /&gt;  BTFSS   STATUS,C&lt;br /&gt;  RETURN&lt;br /&gt;  MOVLW   0xA1&lt;br /&gt;  XORWF   LFSRVALUEH&lt;br /&gt;  XORWF   LFSRVALUEL&lt;br /&gt;  RETURN&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Converting the above into &lt;a href="http://www.mikroe.com/eng/products/view/7/mikroc-pro-for-pic/"&gt;mikroC&lt;/a&gt; PIC16 assembly with the 16-bit random number stored in variable N, we have:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;asm{bcf     STATUS,C&lt;br /&gt;    rrf     _N_L0+1,F&lt;br /&gt;    rrf     _N_L0+0,F&lt;br /&gt;    btfss   STATUS,C&lt;br /&gt;    goto    $+4&lt;br /&gt;    movlw   0xA1&lt;br /&gt;    xorwf   _N_L0+1,F&lt;br /&gt;    xorwf   _N_L0+0,F}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And converting the same into mikroC we have: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;int8 RanNum()&lt;br /&gt;{&lt;br /&gt;  static unsigned int RANDOM = 1;&lt;br /&gt;&lt;br /&gt;  RANDOM &amp;gt;&amp;gt;= 1;&lt;br /&gt;  if (STATUS.C)&lt;br /&gt;    RANDOM ^= 0xA1A1;&lt;br /&gt;  return RANDOM;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code translates into just 8 lines of mikroC assembly using the &lt;a href="http://en.wikipedia.org/wiki/PIC_microcontroller#Enhanced_Mid-range_core_devices"&gt;enhanced midrange instruction set&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For the circuit I'm using it in, I need a number from 0 to 99 to emulate a percentage value. So what I did was to simply take the last two digits (decimal) by dividing the 16-bit number by 100 and taking the remainder. I used a 16-bit generator instead of an 8-bit because the application requires that repetition of the sequence does not occur too quickly. I also wanted the seed number to be changed every time the circuit starts from a power-off condition so that the same sequence doesn't repeat every time the circuit powers up. To implement this the firmware loads RANDOM with the value stored in an EEPROM address. A crucial check is made to make sure RANDOM is not zero. If it is then it's incremented by one. RANDOM is then incremented and stored back in the same EEPROM location. Thus, every time the circuit boots up, RANDOM starts with a different seed.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#define  addr_seed     0x1    // plug in your choice of eeprom address for the seed number&lt;br /&gt;static unsigned int RANDOM;&lt;br /&gt;&lt;br /&gt;void IniSeed()&lt;br /&gt;{&lt;br /&gt;  // seed number for random number generator is obtained from an eeprom location&lt;br /&gt;  // the value of seed number is incremented and stored back in eeprom&lt;br /&gt;  // thus, a different seed number is used every time the MCU powers up.&lt;br /&gt;  RANDOM = EEPROM_Read(addr_seed);&lt;br /&gt;  if (!RANDOM)                           // seed number must not be zero!&lt;br /&gt;    RANDOM++;&lt;br /&gt;  EEPROM_Write(addr_seed, RANDOM+1);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int8 RanNum()&lt;br /&gt;{&lt;br /&gt;  RANDOM &amp;gt;&amp;gt;= 1;&lt;br /&gt;  if (STATUS.C)&lt;br /&gt;    RANDOM ^= 0xA1A1;&lt;br /&gt;  return RANDOM % 100;     // a number from 0 to 99 is returned &lt;br /&gt;} &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that EEPROM_Read() and EEPROM_Write() are mikroC built-in functions. Admittedly the seed number in the above routine is only 8 bits long, but for my particular application this is sufficient. If you need a 16-bit seed then simply read the contents of two EEPROM addresses into RANDOM, increment RANDOM, and store the low and high bytes of RANDOM in the appropriate EEPROM addresses: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;void IniSeed()&lt;br /&gt;{&lt;br /&gt;  RANDOM = EEPROM_Read(addr_seed)*256 + EEPROM_Read(addr_seed + 1);&lt;br /&gt;  if (!RANDOM)                           // seed number must not be zero!&lt;br /&gt;    RANDOM++;&lt;br /&gt;  EEPROM_Write(addr_seed, (RANDOM + 1)/256);&lt;br /&gt;  EEPROM_Write(addr_seed+1, RANDOM + 1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-1802298043863870611?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/1802298043863870611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/random-number-generator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1802298043863870611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1802298043863870611'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/random-number-generator.html' title='Random number generator'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4773706619289517750</id><published>2011-11-23T14:05:00.001+08:00</published><updated>2011-11-23T14:07:02.688+08:00</updated><title type='text'>Variable intermittent wiper control - printed circuit board</title><content type='html'>Finally got around to making a PCB for the &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_928.html"&gt;wiper control&lt;/a&gt;. Used DesignSpark PCB to draw the schematic and PCB layout. The board is 3 x 3.5 inches and was exposed to fluorescent light for 70 seconds. Vertical distance of the two 11-watt Toshiba compact fluorescent lamps from the board was 3 inches. On the long sides, I placed cardboard on which aluminum foil had been taped to reflect light back into the exposure area and even out the lighting. (Up to now I have no idea whether the board is sensitive only to UV or UV + visible light, nor do I know to what degree aluminum reflects/absorbs UV.) Details of the procedure and materials I use can be found in &lt;a href="http://electromotiveforces.blogspot.com/2011/04/making-pcbs-using-presensitized-boards.html"&gt;Making PCBs using presensitized boards&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I introduced one modification to the June 2011 circuit. The wire length from the board to the push button (a tact switch epoxied to the end of the stem switch that controls the headlights and turn signal lights) is almost a meter in length, so instead of directly connecting it to one of the MCU pins, I am now using a &lt;a href="http://www.datasheetcatalog.org/datasheets2/21/218608_1.pdf"&gt;KB817&lt;/a&gt; optoisolator. I also added a red LED to the circuit. It's connected to one of the MCU pins via a current limiting 330-ohm resistor. Right now it serves no function. I just included it just in case and since the MCU pin is unused.&lt;br /&gt;&lt;br /&gt;The Sonalert buzzer isn't in the pics because it's connected via the terminal blocks.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-AMogF-qJpSY/Tsx5rrUQW1I/AAAAAAAAA9A/lfmxYjxVYaM/s1600/viwc-pcb-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-AMogF-qJpSY/Tsx5rrUQW1I/AAAAAAAAA9A/lfmxYjxVYaM/s320/viwc-pcb-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-9IfMf5S78AM/Tsx5y8oJzKI/AAAAAAAAA9M/FMTnfT_SkgI/s1600/viwc-pcb-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-9IfMf5S78AM/Tsx5y8oJzKI/AAAAAAAAA9M/FMTnfT_SkgI/s320/viwc-pcb-2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-I8PX6KqwM-I/Tsx55J5Yz8I/AAAAAAAAA9Y/-xtn20P4ms4/s1600/viwc-pcb-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-I8PX6KqwM-I/Tsx55J5Yz8I/AAAAAAAAA9Y/-xtn20P4ms4/s320/viwc-pcb-3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;As always the soldering leaves much to be desired. I particularly had trouble soldering the huge pins of the &lt;a href="http://www.global-download.schneider-electric.com/85257849002EB8CB/all/52E299CC6CCA0902852578BD005CAE29/$File/28210-en.pdf"&gt;Telemecanique RXM2&lt;/a&gt; DPDT relay to the board. The trick, which I learned only after botching it, is to lay on lots of flux paste on the pins (and copper pads) and melt a huge blob of solder to the puny 30W soldering iron (30-year old, non temperature controlled, and directly plugged into the 220VAC outlet). The thermal mass of the gob of solder and large contact area coupled with the cleaning action of the flux greatly increases the chances of successfully and cleanly soldering these gargantuan pins.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4773706619289517750?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4773706619289517750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/variable-intermittent-wiper-control.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4773706619289517750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4773706619289517750'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/11/variable-intermittent-wiper-control.html' title='Variable intermittent wiper control - printed circuit board'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-AMogF-qJpSY/Tsx5rrUQW1I/AAAAAAAAA9A/lfmxYjxVYaM/s72-c/viwc-pcb-1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4701558919072375693</id><published>2011-10-21T00:24:00.002+08:00</published><updated>2011-11-24T01:12:40.104+08:00</updated><title type='text'>Cascading (snowfall) LED lights</title><content type='html'>I first saw these kinds of lights some two years ago and back then didn't know what they were called so I dubbed them &lt;a href="http://electromotiveforces.blogspot.com/2010/01/raindrop-christmas-lights.html"&gt;raindrop lights&lt;/a&gt;.  Check out the following vids for a couple of variations of cascading lights: &lt;a href="http://youtu.be/FK-wlvhhtXk"&gt;http://youtu.be/FK-wlvhhtXk&lt;/a&gt; and &lt;a href="http://youtu.be/qR4DLUNAbAo"&gt;http://youtu.be/qR4DLUNAbAo&lt;/a&gt;. The first vid has a close-up of the unit, showing--to my surprise--DIP ICs and 1/4 Watt resistors. The LEDs these units use are of the type which can be seen from practically any direction. &lt;br /&gt;&lt;br /&gt;Just as a challenge I wanted to emulate the effect using ordinary white LEDs and a microcontroller. After some trial and error I got the firmware working. I'm sure it could be improved upon and different variations could be implemented. For instance as in the second vid above, the cascade could be made without PWM, leaving fading for the very last LED.&lt;br /&gt;&lt;br /&gt;So here's a vid of the breadboarded circuit. The firmware that drives it follows. If anyone will be so kind enough to donate a real video cam the pic quality can be greatly enhanced&amp;nbsp; :-) &lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/-E80YXJT-XE" width="480"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt;&lt;br /&gt;Cascading (Snowfall or Meteor) LED Lights&lt;br /&gt;October 16 2011&lt;br /&gt;&lt;br /&gt;processor = PIC16F1827&lt;br /&gt;compiler = mikroC v5.0.0&lt;br /&gt;&lt;br /&gt;(configuration settings -- copied from mikroC's "Edit Project" window)&lt;br /&gt;CONFIG1   :$8007 : 0x0F84&lt;br /&gt;CONFIG2   :$8008 : 0x1613&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// if using different PORT pins just change the assignments below&lt;br /&gt;// the firmware does not need to be edited&lt;br /&gt;#define  led1                LATA.f1             // led1 is the first LED to be lit (topmost LED of the cascade)&lt;br /&gt;#define  led2                LATA.f0&lt;br /&gt;#define  led3                LATA.f7&lt;br /&gt;#define  led4                LATA.f6&lt;br /&gt;#define  led5                LATB.f7&lt;br /&gt;#define  led6                LATB.f6&lt;br /&gt;#define  led7                LATB.f5&lt;br /&gt;#define  led8                LATB.f4&lt;br /&gt;#define  led9                LATB.f3&lt;br /&gt;#define  led10               LATB.f2&lt;br /&gt;#define  led11               LATB.f1&lt;br /&gt;#define  led12               LATB.f0&lt;br /&gt;#define  led13               LATA.f4&lt;br /&gt;#define  led14               LATA.f3&lt;br /&gt;#define  led15               LATA.f2             // led15 is the last LED to be lit (bottom LED of the cascade)&lt;br /&gt;#define  lastled             led15               // this is used in the Twinkle() function&lt;br /&gt;&lt;br /&gt;// following used for twinkle effect&lt;br /&gt;#define  fadeintime          2                   // number of PWM periods for each pulse width during fade in from min to max brightness&lt;br /&gt;#define  fadeouttime         7                   // number of PWM periods for each pulse width during fade out from max to min brightness&lt;br /&gt;#define  twinkle_delay       1                   // deciseconds between the end of the cascade and beginning of twinkle effect&lt;br /&gt;                                                 // during this time led15 is off&lt;br /&gt;&lt;br /&gt;#define  cascade_delay       20                  // deciseconds between cascades. during this time all LEDs are off&lt;br /&gt;                                                 // time begins after Twinkle()&lt;br /&gt;&lt;br /&gt;#define  t0_ini              256-128             // TMR0 initialize value every time it overflows&lt;br /&gt;#define  t1h_ini             256-98              // TMR1H initialize value every time it overflows&lt;br /&gt;#define  cascadetime         10                  // number of cycles (PWM periods) per stage&lt;br /&gt;#define  maxstage            22                  // maximum number of stages&lt;br /&gt;                                                 // a stage is when a set of LEDs are lit&lt;br /&gt;                                                 // each successive stage moves the cascade by one LED&lt;br /&gt;&lt;br /&gt;#define  int1                bit&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;#define  int32               unsigned long&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;void IniReg()&lt;br /&gt;{&lt;br /&gt;  OSCCON = 0b1110000;        // 8MHz&lt;br /&gt;  TRISA = 0;&lt;br /&gt;  TRISB = 0;&lt;br /&gt;  ANSELA = 0;&lt;br /&gt;  ANSELB = 0;&lt;br /&gt;  LATA = 0;&lt;br /&gt;  LATB = 0;&lt;br /&gt;  T1CON = 0b110000;          // prescale = 1:8, timer1 off&lt;br /&gt;                             // given 8MHz, TMR1H = 98 counts, prescale = 1:8, timer1 tick = ~100ms&lt;br /&gt;} // void IniReg()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// During the start of each PWM period the LEDs that will be "PWMed" for that stage are turned on.&lt;br /&gt;void IniLEDs(int8 STAGE)&lt;br /&gt;{&lt;br /&gt;  switch (STAGE)&lt;br /&gt;  {&lt;br /&gt;    case 1:&lt;br /&gt;      led1 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 2:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 3:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 4:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 5:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 6:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 7:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 8:&lt;br /&gt;      led1 = on;&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 9:&lt;br /&gt;      led2 = on;&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 10:&lt;br /&gt;      led3 = on;&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 11:&lt;br /&gt;      led4 = on;&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 12:&lt;br /&gt;      led5 = on;&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 13:&lt;br /&gt;      led6 = on;&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 14:&lt;br /&gt;      led7 = on;&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 15:&lt;br /&gt;      led8 = on;&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 16:&lt;br /&gt;      led9 = on;&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 17:&lt;br /&gt;      led10 = on;&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 18:&lt;br /&gt;      led11 = on;&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 19:&lt;br /&gt;      led12 = on;&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 20:&lt;br /&gt;      led13 = on;&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 21:&lt;br /&gt;      led14 = on;&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 22:&lt;br /&gt;      led15 = on;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      break;&lt;br /&gt;  } // switch (STAGE)&lt;br /&gt;} //void IniLEDs()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// Within the PWM period, LEDs are turned off when their particular pulse width has been reached.&lt;br /&gt;void SwitchLEDs(int8 STAGE, int8 PWEXP)&lt;br /&gt;{&lt;br /&gt;  switch (PWEXP)&lt;br /&gt;  {&lt;br /&gt;    case 0:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 8:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 18:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 19:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 20:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 21:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 22:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 1:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 7:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 18:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 19:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 20:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 21:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 2:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 6:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 7:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 18:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 19:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 20:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 3:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 5:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 6:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 7:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 18:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 19:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 4:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 4:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 5:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 6:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 7:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 18:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 5:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 3:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 4:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 5:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 6:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 7:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 17:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case 6:&lt;br /&gt;      switch (STAGE)&lt;br /&gt;      {&lt;br /&gt;        case 2:&lt;br /&gt;          led1 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 3:&lt;br /&gt;          led2 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 4:&lt;br /&gt;          led3 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 5:&lt;br /&gt;          led4 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 6:&lt;br /&gt;          led5 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 7:&lt;br /&gt;          led6 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 8:&lt;br /&gt;          led7 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 9:&lt;br /&gt;          led8 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 10:&lt;br /&gt;          led9 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 11:&lt;br /&gt;          led10 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 12:&lt;br /&gt;          led11 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 13:&lt;br /&gt;          led12 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 14:&lt;br /&gt;          led13 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 15:&lt;br /&gt;          led14 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        case 16:&lt;br /&gt;          led15 = off;&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;        default:&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    // for PWEXP = 7, no led need be switched off since it's duty cycle will be gradually decreased anyway in the next STAGE&lt;br /&gt;    // therefore there's no need for a separate case statement. &lt;br /&gt;    // The default option below will automatically "catch" the case when PWEXP = 7&lt;br /&gt;&lt;br /&gt;    default:&lt;br /&gt;      break;&lt;br /&gt;  } // switch (PULSEWIDTH)&lt;br /&gt;} // void SwitchLEDs()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;The particular LEDs to be lit and their pulse width depends on the current STAGE. &lt;br /&gt;At the start of each PWM period all LEDs for the particular stage are turned on.&lt;br /&gt;Then one by one, beginning with the LED that has the least pulse width, they're turned off. &lt;br /&gt;After the second TMR0 overflow, the prescale value of timer0 is changed every TMR0 overflow as shown in the table below. &lt;br /&gt;Every time TMR0 overflows it is re-initialized with t0_ini (128decimal).&lt;br /&gt;The total time elapsed is the pulse width of the LEDs. &lt;br /&gt;The change in PW between the LEDs is exponential. &lt;br /&gt;The lead LED has a duty cycle of 100%.&lt;br /&gt;&lt;br /&gt;PWN period = 8.192ms &lt;br /&gt;Fosc = 8Mhz&lt;br /&gt;t0 tick = (TMR0 count)*(prescale)/(Fosc/4)&lt;br /&gt;&lt;br /&gt;t0_ini  TMR0 count   prescale   t0 tick (us)  total time elapsed (us) &lt;br /&gt;128        128           1           64               64 &lt;br /&gt;128        128           1           64              128 &lt;br /&gt;128        128           2          128              256 &lt;br /&gt;128        128           4          256              512 &lt;br /&gt;128        128           8          512             1024 &lt;br /&gt;128        128          16         1024             2048 &lt;br /&gt;128        128          32         2048             4096 &lt;br /&gt;128        128          64         4096             8192 &lt;br /&gt;*/&lt;br /&gt;void Cascade()&lt;br /&gt;{&lt;br /&gt;  // OPTREG[] lookup table contains values to be successively loaded into OPTION register during one PWM period&lt;br /&gt;  // timer0 prescale values are {1,1,2,4,8,16,32,64}&lt;br /&gt;  // to obtain timer0 prescale value = 1, prescaler is NOT assigned to timer0&lt;br /&gt;  // timer0 initial value is kept constant at 128decimal&lt;br /&gt;  const int8 OPTREG[] = {0b1000, 0b1000, 0b0, 0b1, 0b10, 0b11, 0b100, 0b101};&lt;br /&gt;&lt;br /&gt;  int8 STAGE;                  // at which stage the cascade is currently&lt;br /&gt;  int8 PWEXP;                  // exponent (with base = 2) of the pulse width&lt;br /&gt;  int8 TOTALTIME;              // records number of PWM periods&lt;br /&gt;&lt;br /&gt;  PWEXP = 0;&lt;br /&gt;  for (STAGE = 1; STAGE &amp;lt;= maxstage; STAGE++)&lt;br /&gt;  {&lt;br /&gt;    TOTALTIME = 0;&lt;br /&gt;    while (TOTALTIME &amp;lt;= cascadetime)&lt;br /&gt;    {&lt;br /&gt;      if (!PWEXP)                                // initialize (turn on) LEDs only when it's the start of a PWM period&lt;br /&gt;        IniLEDs(STAGE);&lt;br /&gt;      OPTION_REG = OPTREG[PWEXP];&lt;br /&gt;      TMR0 = t0_ini;&lt;br /&gt;      INTCON.T0IF = 0;&lt;br /&gt;      while (!INTCON.T0IF) ;&lt;br /&gt;      SwitchLEDs(STAGE, PWEXP);                   // turn off the LED&lt;br /&gt;      if (++PWEXP &amp;gt; 7)&lt;br /&gt;      {&lt;br /&gt;        PWEXP = 0;&lt;br /&gt;        TOTALTIME++;&lt;br /&gt;      }&lt;br /&gt;    } // while (TOTALTIME &amp;lt;= cascadetime)&lt;br /&gt;  } // for (STAGE=1; STAGE&amp;lt;= maxstage; STAGE++)&lt;br /&gt;} // void Cascade()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// PWM routine for the twinkle effect&lt;br /&gt;void Dim(int8 EXP, int8 FADETIME)&lt;br /&gt;{&lt;br /&gt;  // pulse width (output high = led on) in terms of the value of TMR0, given a fixed timer0 period of 8.192ms&lt;br /&gt;  const int8 PW[] = {1, 2, 4, 8, 16, 32, 64, 128, 255};&lt;br /&gt;&lt;br /&gt;  int8 TOTALTIME = 0;&lt;br /&gt;  int8 PULSEWIDTH;&lt;br /&gt;&lt;br /&gt;  TMR0 = 0;&lt;br /&gt;  INTCON.T0IF = 0;&lt;br /&gt;  PULSEWIDTH = PW[EXP];&lt;br /&gt;&lt;br /&gt;  while (TOTALTIME++ &amp;lt;= FADETIME)&lt;br /&gt;  {&lt;br /&gt;    lastled = on;&lt;br /&gt;    while (TMR0 &amp;lt; PULSEWIDTH);&lt;br /&gt;    lastled = off;&lt;br /&gt;    while (!INTCON.T0IF);&lt;br /&gt;    INTCON.T0IF = 0;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// twinkle effect where fade in time and fade out time are or can be different&lt;br /&gt;void Twinkle()&lt;br /&gt;{&lt;br /&gt;  int8 i;&lt;br /&gt;  &lt;br /&gt;  OPTION_REG = 0b10000101;             // timer0 prescale = 1:64&lt;br /&gt;  for (i = 0; i &amp;lt;= 8; i++)&lt;br /&gt;    Dim(i, fadeintime);&lt;br /&gt;  for (i = 7; i &amp;gt; 0; i--)&lt;br /&gt;    Dim(i, fadeouttime);&lt;br /&gt;  lastled = off;&lt;br /&gt;} // void Twinkle()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// delay in deciseconds&lt;br /&gt;// as an alternative timer0 can be used instead of timer1 if, say, using an MCU with only one timer&lt;br /&gt;void DelayDsec(int8 dsec)&lt;br /&gt;{&lt;br /&gt;  int8 i;&lt;br /&gt;&lt;br /&gt;  TMR1L = 0;&lt;br /&gt;  T1CON.TMR1ON = 1;&lt;br /&gt;  for (i = 0; i &amp;lt; dsec; i++)&lt;br /&gt;  {&lt;br /&gt;    TMR1H = t1h_ini;&lt;br /&gt;    PIR1.TMR1IF = 0;&lt;br /&gt;    while (!PIR1.TMR1IF);&lt;br /&gt;  }&lt;br /&gt;  T1CON.TMR1ON = 0;&lt;br /&gt;} // void DeadTime()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  IniReg();&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    Cascade();&lt;br /&gt;    DelayDsec(twinkle_delay);&lt;br /&gt;    Twinkle();&lt;br /&gt;    DelayDsec(cascade_delay);&lt;br /&gt;  }&lt;br /&gt;} // void main()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;November 23 2011 Addendum&lt;br /&gt;&lt;br /&gt;Here's the schematic for the cascading lights circuit. As you can see it's just one MCU and LEDs on all the pins except for pin#4 (RA5) which is an input-only pin. Resistors can be as low as, oh about, 180 ohms for a power supply of 5 volts. You have to watch out for the maximum rating of each pin (25mA if I remember correctly) and the maximum for each PORT. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7DcKE5_73YU/Tsvp3TxmUSI/AAAAAAAAA80/Xf_tU1KY1KQ/s1600/snowfall-lights-schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="141" src="http://1.bp.blogspot.com/-7DcKE5_73YU/Tsvp3TxmUSI/AAAAAAAAA80/Xf_tU1KY1KQ/s320/snowfall-lights-schematic.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;Schematic when using a PIC16F84A&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-wQhknD9-fMs/Ts0ojs_WnnI/AAAAAAAAA9k/aKLF5vut86I/s1600/snowfall-lights-schematic%2BPIC16F84A.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="136" src="http://4.bp.blogspot.com/-wQhknD9-fMs/Ts0ojs_WnnI/AAAAAAAAA9k/aKLF5vut86I/s320/snowfall-lights-schematic%2BPIC16F84A.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4701558919072375693?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4701558919072375693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/cascading-snowfall-led-lights.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4701558919072375693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4701558919072375693'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/cascading-snowfall-led-lights.html' title='Cascading (snowfall) LED lights'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/-E80YXJT-XE/default.jpg' height='72' width='72'/><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-9098938493931306369</id><published>2011-10-13T17:25:00.001+08:00</published><updated>2011-10-14T12:38:52.278+08:00</updated><title type='text'>Video of LED fade in/out</title><content type='html'>For details of the following vid see yesterday's &lt;a href="http://electromotiveforces.blogspot.com/2011/10/led-fading-using-exponentially-changing.html"&gt;LED fading using exponentially changing PWM duty cycle&lt;/a&gt;. However, because the dimming wasn't as smooth as I wanted it to be, I changed a couple of items.&lt;br /&gt;&lt;br /&gt;1. I used 32 pulse width values instead of 16. And so the look-up table for this demo is:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;const char PW[] = {0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 22, 26, 31, 37, 44, 53, 63, 75, 90, 107, 127, 151, 180, 214, 255};&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;2. To preserve the 655ms total time from zero to maximum brightness I halved the timer2 postscale to 1:5. &lt;br /&gt;&lt;br /&gt;3. Because of the increase in PW values, the constant in one of the conditional statements had to be changed from 15 to 31:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;if (direction &amp;amp;&amp;amp; ++i &amp;gt; 31)&lt;/code&gt;.&lt;/blockquote&gt;&lt;br /&gt;&lt;iframe width="480" height="360" src="http://www.youtube.com/embed/WQWeU8mntNw" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-9098938493931306369?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/9098938493931306369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/video-of-led-fade-inout.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/9098938493931306369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/9098938493931306369'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/video-of-led-fade-inout.html' title='Video of LED fade in/out'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/WQWeU8mntNw/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-8463987042291568237</id><published>2011-10-13T00:12:00.001+08:00</published><updated>2011-10-13T00:44:48.265+08:00</updated><title type='text'>LED fading using exponentially changing PWM duty cycle</title><content type='html'>Not wishing to make the title any longer than it is, I left out the following qualification: Although the duty cycle (pulse width for output high) varies exponentially versus time, the time interval between changes in duty cycle is fixed. &lt;br /&gt;&lt;br /&gt;One could I suppose also achieve fading with a linearly changing duty cycle and exponentially changing time interval. Sounds more complicated though. &lt;br /&gt;&lt;br /&gt;The problem with fading an LED using PWM is that human perception of brightness is logarithmic, just like our hearing. So changing the PWM duty cycle in a linear fashion (e.g. incrementing or decrementing the duty cycle by 1% every unit time interval) does not result in a perception of linear change in brightness. &lt;br /&gt;&lt;br /&gt;The solution is to change the duty cycle exponentially. The following is one formula that provides a way of obtaining a series of duty cycle values with a minimum of 1 and a maximum of 2&lt;sup&gt;n&lt;/sup&gt;, where n is a positive integer. The fact that the numeric base = 2 means the results are perfectly suited to computing values for 8, 16, or 32-bit registers of microcontrollers. &lt;br /&gt;&lt;br /&gt;duty cycle = 2&lt;sup&gt;x/r&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;where&lt;br /&gt;x = 1, 2, 3, ..., m-1, m&lt;br /&gt;m = integral number of duty cycle values required.&lt;br /&gt;r is chosen such that 2&lt;sup&gt;m/r&lt;/sup&gt; = maximum duty cycle value&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/--90Mju_aQwY/TpW8A97vqhI/AAAAAAAAA8U/A_1Bq7khIFU/s1600/ruler6_quarter_41629_md.gif" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="64" src="http://2.bp.blogspot.com/--90Mju_aQwY/TpW8A97vqhI/AAAAAAAAA8U/A_1Bq7khIFU/s320/ruler6_quarter_41629_md.gif" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;One way to understand x/r is to think of a number of rulers all of the same length--say, 12 inches. One has graduations only in inches. Another has divisions only in half inches (of course two half inches still make an inch). Yet another is marked off in quarter inches. One has one-eighth inch graduations. And another is divided into sixteenths of an inch. x corresponds to the number of graduations/divisions. Hence our 12-inch ruler with quarter-inch divisions has a total of 12 inches x 4 divisions/inch = 48 divisions. And the ruler with one-sixteenth inch graduations has 12 x 16 = 192 divisions. On the other hand, 1/r corresponds to the unit used by our ruler or the distance between graduations. So for inch-graduated ruler, 1/r = 1/1. For the one-eighth-inch-graduated ruler, 1/r = 1/8. &lt;br /&gt;&lt;br /&gt;Let's say we are constrained to using an 8-bit register (eg. CCPR1L of the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en027148"&gt;PIC12F615&lt;/a&gt;) for our duty cycle values. This means the maximum pulse width value we can specify is 0xFF or 255 decimal. Assume we want to make an LED fade in from zero to full brightness in a relatively short time--less than half a second. Having the LED ramp up 16 values should be sufficient for a visually smooth, seamless fade in. Going back to our ruler, if we employ the ruler with one-inch divisions, we'd have eight values and those values, as per our formula, would be 2&lt;sup&gt;1/1&lt;/sup&gt;, 2&lt;sup&gt;2/1&lt;/sup&gt;, ... 2&lt;sup&gt;8/1&lt;/sup&gt;. But since we'd like 16 different pulse widths, we have to use the half-inch-graduated ruler which has 16 divisions from 1/2 inch to 8 inches. To obtain the actual duty cycle values, we use the formula  giving us the following series: 2&lt;sup&gt;1/2&lt;/sup&gt;, 2&lt;sup&gt;2/2&lt;/sup&gt;, 2&lt;sup&gt;3/2&lt;/sup&gt;, ... 2&lt;sup&gt;15/2&lt;/sup&gt;, 2&lt;sup&gt;16/2&lt;/sup&gt;, or 1.41, 2.00, 2.83, .... 181.02, 256.00. Rounding those numbers off we have the following integral series: 1, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 91, 128, 181, 255. Obviously, given our 8-bit constraint, the maximum duty cycle value has to be 255 not 256. &lt;br /&gt;&lt;br /&gt;If we wanted 32 pulse width values--perhaps because we are fading the LED over a much longer time interval and 16 values don't provide a smooth enough fade--we'd choose the ruler with quarter-inch divisions. Using the equation we have: 2&lt;sup&gt;1/4&lt;/sup&gt;, 2&lt;sup&gt;2/4&lt;/sup&gt;, 2&lt;sup&gt;3/4&lt;/sup&gt;, 2&lt;sup&gt;4/4&lt;/sup&gt;, 2&lt;sup&gt;5/4&lt;/sup&gt;, ... 2&lt;sup&gt;31/4&lt;/sup&gt;, 2&lt;sup&gt;32/4&lt;/sup&gt;. Here's a graph of this example. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-PTc0PdukVsA/TpWoXgNwPKI/AAAAAAAAA8A/Asvlm_W0Dqk/s1600/exponential%2Bduty%2Bcycle%2Bvalues.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="370" src="http://2.bp.blogspot.com/-PTc0PdukVsA/TpWoXgNwPKI/AAAAAAAAA8A/Asvlm_W0Dqk/s400/exponential%2Bduty%2Bcycle%2Bvalues.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Bear in mind that the ruler analogy is only an aid to understanding. Once you get the hang of the equation, you can throw away these imaginary rulers. And contrary to what the ruler examples imply, we're not limited to 2&lt;sup&gt;n&lt;/sup&gt; number of pulse widths (eg. 8, 16, 32, 64). Still taking our maximum duty cycle = 0xFF, we can have, for instance, 24 values: 2&lt;sup&gt;1/3&lt;/sup&gt;, 2&lt;sup&gt;2/3&lt;/sup&gt;, 2&lt;sup&gt;3/3&lt;/sup&gt;, 2&lt;sup&gt;4/3&lt;/sup&gt;, 2&lt;sup&gt;5/3&lt;/sup&gt;, ... 2&lt;sup&gt;23/3&lt;/sup&gt;, 2&lt;sup&gt;24/3&lt;/sup&gt;. Or, say, 40 values: 2&lt;sup&gt;1/5&lt;/sup&gt;, 2&lt;sup&gt;2/5&lt;/sup&gt;, ... 2&lt;sup&gt;13/5&lt;/sup&gt;, 2&lt;sup&gt;14/5&lt;/sup&gt;, 2&lt;sup&gt;15/5&lt;/sup&gt;, ... 2&lt;sup&gt;39/5&lt;/sup&gt;, 2&lt;sup&gt;40/5&lt;/sup&gt;. Or 56 values: 2&lt;sup&gt;1/7&lt;/sup&gt;, 2&lt;sup&gt;2/7&lt;/sup&gt;, ... 2&lt;sup&gt;55/7&lt;/sup&gt;, 2&lt;sup&gt;56/7&lt;/sup&gt;. Notice that for all these examples m/r is still = 8 and so maximum duty cycle value in all these series is 0xFF. &lt;br /&gt;&lt;br /&gt;One may, if desired, also use the following slightly modified formula&lt;br /&gt;&lt;br /&gt;duty cycle = 2&lt;sup&gt;x/r&lt;/sup&gt; - 1&lt;br /&gt;&lt;br /&gt;Using this equation forces the series to start with zero.&lt;br /&gt;&lt;br /&gt;Given m = 16, r = 2, and thus a maximum duty cycle value of 2&lt;sup&gt;16/2&lt;/sup&gt; = 0xFF, the series we end up with is: 0, 1, 2, 3, 5, 7, 10, 15, 22, 31, 44, 63, 90, 127, 180, 255. Note how the maximum is neatly the maximum possible for our 8-bit register. &lt;br /&gt;&lt;br /&gt;Below is a sample program where an LED alternately fades in and out. The firmware employs a look-up table to change the duty cycle. PWM frequency is 244.14 Hz. This is the minimum PWM frequency obtainable in the PIC12F615 when using its internal oscillator (Fosc = 4MHz). That PWM frequency translates to a period of 4.096ms. Because 4.096ms x 16 duty cycle values is only = 65.5ms, I set timer2 postscaler to 1:10 which has the effect of holding each duty cycle value for 10 periods. With that the amount of time for the LED to go from minimum to maximum duty cycle = 4.096ms x 16 x 10 = 655ms which is slow enough for humans to actually see and appreciate. The downside is that keeping the DC value for 10 periods is noticeable at the lower DC values. The stepwise change is obvious. To remedy this, we can always increase the number of DC values and decrease the timer2 postscale value. &lt;br /&gt;&lt;br /&gt;For the PIC12F615, &lt;br /&gt;&lt;br /&gt;f&lt;sub&gt;PWM&lt;/sub&gt; = Fosc / [(PR2+1)*4*(T2PS)]&lt;br /&gt;&lt;br /&gt;f&lt;sub&gt;PWM&lt;/sub&gt; = PWM frequency&lt;br /&gt;Fosc = frequency of the oscillator&lt;br /&gt;PR2 = PR2 register value&lt;br /&gt;T2PS = timer2 prescale value&lt;br /&gt;&lt;br /&gt;PWM period = 1 / f&lt;sub&gt;PWM&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;Pulse width = (CCPR1L:CCP1CON&amp;lt;5:4&amp;gt;)*(T2PS)/Fosc&lt;br /&gt;&lt;br /&gt;CCPR1L:CCP1CON&amp;lt;5:4&amp;gt; is a 10-bit number, with the 8-bit MSB in CCPR1L and the 2-bit LSB in CCP1CON&amp;lt;5:4&amp;gt;&lt;br /&gt;&lt;br /&gt;In the firmware below CCP1CON&amp;lt;5:4&amp;gt; is = 0. When using the pulse width equation remember to left shift CCPR1L by two places. In other words multiply CCPR1L by 4. &lt;br /&gt;&lt;br /&gt;LED is connected to pin P1A/GP2. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt;LED fading using exponentially changing PWM duty cycle with fixed time interval between changes in duty cycle&lt;br /&gt;&lt;br /&gt;October 12 2011&lt;br /&gt;&lt;br /&gt;processor = PIC12F615&lt;br /&gt;compiler = mikroC v5.0.0&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;// computed duty cycle values using the equation INT{[2^(x/r)] + 0.5} - 1&lt;br /&gt;// x = 1, 2, ..., 16&lt;br /&gt;// r = 2&lt;br /&gt;// values are stored in a look-up table in flash rather than RAM&lt;br /&gt;const char PW[] = {0, 1, 2, 3, 5, 7, 10, 15, 22, 31, 44, 63, 90, 127, 180, 255};  &lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  unsigned char i = 0;&lt;br /&gt;  bit direction;&lt;br /&gt;  &lt;br /&gt;  ANSEL = 0;                 // all pins digital&lt;br /&gt;&lt;br /&gt;  // initialize PWM registers&lt;br /&gt;  // see PIC12F615 datasheet DS41302C p.96 for PWM set up procedure&lt;br /&gt;  PR2 = 255;&lt;br /&gt;  CCP1CON = 0b1100;          // enable PWM mode, single output (P1A only)&lt;br /&gt;  CCPR1L = 0;&lt;br /&gt;  PIR1.TMR2IF = 0;           // clear timer2 interrupt flag&lt;br /&gt;  T2CON = 0b1001011;         // timer2 prescale = 1:16, postscale = 1:10&lt;br /&gt;  T2CON.TMR2ON = 1;          // turn on timer2&lt;br /&gt;  TRISIO = 0b0;&lt;br /&gt;&lt;br /&gt;  direction = 1;&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (PIR1.TMR2IF)          // timer2 interrupt flag is set when postscale value is reached &lt;br /&gt;    {&lt;br /&gt;      if (direction &amp;amp;&amp;amp; ++i &amp;gt; 15)&lt;br /&gt;      {&lt;br /&gt;        direction = 0;&lt;br /&gt;        i = 14;&lt;br /&gt;      }&lt;br /&gt;      else if (!direction &amp;amp;&amp;amp; --i == 0)&lt;br /&gt;        direction = 1;&lt;br /&gt;      CCPR1L = PW[i];&lt;br /&gt;      PIR1.TMR2IF = 0;&lt;br /&gt;    }&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;References:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.instructables.com/community/linear-PWM-LED-fade-with-arduino/"&gt;http://www.instructables.com/community/linear-PWM-LED-fade-with-arduino/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=printview&amp;amp;t=45080&amp;amp;start=0"&gt;http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=printview&amp;amp;t=45080&amp;amp;start=0&lt;/a&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/F-number"&gt;http://en.wikipedia.org/wiki/F-number&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.mikroe.com/app/webroot/forum/viewtopic.php?f=88&amp;amp;t=22277"&gt;http://www.mikroe.com/app/webroot/forum/viewtopic.php?f=88&amp;amp;t=22277&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-8463987042291568237?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/8463987042291568237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/led-fading-using-exponentially-changing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8463987042291568237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8463987042291568237'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/10/led-fading-using-exponentially-changing.html' title='LED fading using exponentially changing PWM duty cycle'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/--90Mju_aQwY/TpW8A97vqhI/AAAAAAAAA8U/A_1Bq7khIFU/s72-c/ruler6_quarter_41629_md.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-26622438124351252</id><published>2011-09-27T00:18:00.001+08:00</published><updated>2011-09-27T01:59:41.719+08:00</updated><title type='text'>Checking out the Pierce oscillator</title><content type='html'>The Pierce oscillator is a very simple circuit and as with the &lt;a href="http://electromotiveforces.blogspot.com/2011/09/taking-look-at-basic-ring-oscillators.html"&gt;ring oscillator&lt;/a&gt; also uses inverters (actually just one inverter will do). The principle of operation, however, is very different. While the ring oscillator exploits the incremental propagation delay through the series of inverters, the Pierce oscillator puts the inverter/gate into the linear region of operation and employs it as an analog amplifier.&lt;br /&gt;&lt;br /&gt;To find out the details of the gate-based Pierce oscillator I recommend reading the references listed below. What follows is just a summary of the tips and formulas culled from the said references, pointers and tidbits which I found useful in getting a  crystal-based oscillator up and running. I also did a number of breadboarded experiments and the results are shown below. &lt;br /&gt;&lt;br /&gt;The following schematic shows the basic Pierce oscillator based on a CMOS inverter as its amplifier.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-mAmLljbgTAY/Tn7WVO2MNgI/AAAAAAAAA3g/7xNOq1-c-AU/s1600/pierce-osc-cmos-inverter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="182" src="http://1.bp.blogspot.com/-mAmLljbgTAY/Tn7WVO2MNgI/AAAAAAAAA3g/7xNOq1-c-AU/s200/pierce-osc-cmos-inverter.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;INV is of course the inverter (buffered or unbuffered). Oscillator signal is taken from the inverter output (V&lt;sub&gt;out&lt;/sub&gt;).  To improve (decrease) its rise/fall time, another inverter or, better  yet, a (fast!) Schmitt trigger can be used to spruce up the signal. The signal at the inverter input is a sinusoid and can also be used (some Microchip MCU datasheets show the clock signal being derived from the input of the inverter while other Microchip datasheets show it being taken from the inverter output).&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;f&lt;/sub&gt; is a feedback resistor that puts the gate in linear (as opposed to digital) mode operation. The following &lt;a href="http://www.mpdigest.com/issue/Articles/2008/Mar/Crystek/Default.asp"&gt;table&lt;/a&gt; provides a rule of thumb value for R&lt;sub&gt;f&lt;/sub&gt; given crystal frequency:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-JIMl4Zy7imI/Tn6SDcyss4I/AAAAAAAAA3Y/39e1vsibHzc/s1600/pierce%2Boscillator%2Bfeedback%2Bresistor%2Btable.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="226" src="http://2.bp.blogspot.com/-JIMl4Zy7imI/Tn6SDcyss4I/AAAAAAAAA3Y/39e1vsibHzc/s400/pierce%2Boscillator%2Bfeedback%2Bresistor%2Btable.jpg" width="278" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Among other things, R&lt;sub&gt;s&lt;/sub&gt; limits the amount of crystal drive--increasing R&lt;sub&gt;s&lt;/sub&gt; decreases drive. A ballpark figure or first cut value for R&lt;sub&gt;s&lt;/sub&gt; can be derived by computing for and equating R&lt;sub&gt;s&lt;/sub&gt; to the reactance of C&lt;sub&gt;b&lt;/sub&gt;:&lt;br /&gt;&lt;blockquote&gt;X&lt;sub&gt;c&lt;sub&gt;b&lt;/sub&gt;&lt;/sub&gt; = 1/(2πfC&lt;sub&gt;b&lt;/sub&gt;), where f = crystal frequency&lt;/blockquote&gt;According to &lt;a href="http://ww1.microchip.com/downloads/en/AppNotes/00849a.pdf"&gt;Microchip&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;R&lt;sub&gt;s&lt;/sub&gt; is typically 40 K ohms or less, but is almost never more than 100 K ohms. If the value for R&lt;sub&gt;s&lt;/sub&gt;  is too high, then the high impedance input side of the amplifier may be more susceptible to noise, very much the same way a pull-up resistor on an input pin is normally kept below about 50 K ohms to prevent noise from having enough strength to override the input.&lt;/blockquote&gt;&lt;a href="http://cache.freescale.com/files/microcontrollers/doc/app_note/AN1706.pdf"&gt;Freescale&lt;/a&gt; (Motorola) meanwhile claims that for low frequencies such as 32.768kHz watch crystals, R&lt;sub&gt;s&lt;/sub&gt; can go as high as 330Kohms. Because of a permissible maximum drive of just 1µW, a minimum R&lt;sub&gt;s&lt;/sub&gt; value for tuning fork crystals such as the 32.768kHz crystal is 10kohm. &lt;br /&gt;&lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; and C&lt;sub&gt;b&lt;/sub&gt; along with R&lt;sub&gt;s&lt;/sub&gt; and XTAL provide a 180 degree phase shift (INV provides the other 180 degrees for a total of 360). Moreover, the R&lt;sub&gt;s&lt;/sub&gt; and C&lt;sub&gt;b&lt;/sub&gt; network partly "acts as a low pass filter that discourages the crystal from running at a third or fifth harmonic, or other higher frequency" (Lancaster and Berlin). Increasing C&lt;sub&gt;a&lt;/sub&gt; and C&lt;sub&gt;b&lt;/sub&gt;  decreases the gain. C&lt;sub&gt;a&lt;/sub&gt; and C&lt;sub&gt;b&lt;/sub&gt; are usually equal but C&lt;sub&gt;b&lt;/sub&gt; can be made larger than C&lt;sub&gt;a. &lt;/sub&gt;The voltage at the input of INV is (partly) determined by C&lt;sub&gt;b&lt;/sub&gt; / C&lt;sub&gt;a&lt;/sub&gt;, so that increasing C&lt;sub&gt;b&lt;/sub&gt; relative to Ca&lt;sub&gt;&lt;/sub&gt; increases the voltage at INV input.&amp;nbsp;As a rule the values of C&lt;sub&gt;a &lt;/sub&gt;and C&lt;sub&gt;b&lt;/sub&gt; should satisfy the following condition:&lt;br /&gt;&lt;blockquote&gt;C&lt;sub&gt;load&lt;/sub&gt; = (C&lt;sub&gt;a&lt;/sub&gt;)(C&lt;sub&gt;b&lt;/sub&gt;)/(C&lt;sub&gt;a&lt;/sub&gt; + C&lt;sub&gt;b&lt;/sub&gt;) + stray capacitance&lt;/blockquote&gt;where C&lt;sub&gt;load&lt;/sub&gt; is the load capacitance of the crystal as per manufacturer's specifications. Typical load capacitances are 12 pF, 15 pF, 18 pF, 20 pF, 22 pF and 32 pF.&lt;br /&gt;&lt;br /&gt;Finally, an &lt;a href="http://www.fairchildsemi.com/an/AN/AN-340.pdf"&gt;advice&lt;/a&gt; worth keeping in mind: "Oscillator design is an imperfect art at best. Combinations of theoretical and experimental design techniques should be used." And so we move on to the experimental side of things.&lt;br /&gt;&lt;br /&gt;I performed a few tests on a breadboard using 4.000 MHz and 32.768 kHz crystals. The values for the resistors and caps are the ones I arrived at after some trial and error. The values below seem to work better. Yes, that's a very subjective assessment and shall leave it at that.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;I. Motorola &lt;a href="http://www.datasheetcatalog.org/datasheet2/a/0s6fgf8728z3o4kzqz87qk4uzpcy.pdf"&gt;MC14049UBCP&lt;/a&gt; hex inverting buffer&lt;/b&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-AS6CevP-rO4/Tn8um5qRkVI/AAAAAAAAA3o/YGo3KUgiHtE/s1600/pierce-osc-cmos-2inverters.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="243" src="http://2.bp.blogspot.com/-AS6CevP-rO4/Tn8um5qRkVI/AAAAAAAAA3o/YGo3KUgiHtE/s320/pierce-osc-cmos-2inverters.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;A. XTAL = 32.768 kHz&lt;/b&gt;&lt;br /&gt;R&lt;sub&gt;f&lt;/sub&gt; = 10Mohm&lt;br /&gt;Rs = 100kohm&lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; = 33pF&lt;br /&gt;C&lt;sub&gt;b&lt;/sub&gt; = 50pF&lt;br /&gt;&lt;br /&gt;Oscilloscope setup&lt;br /&gt;Channel 1 (yellow) hooked up to INV2 output&lt;br /&gt;Channel 2 (cyan) probe hooked up to C&lt;sub&gt;b&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-srWZKxJNlRU/ToAQRW_XROI/AAAAAAAAA4I/aaWoaOQdWUc/s1600/NewFile0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-srWZKxJNlRU/ToAQRW_XROI/AAAAAAAAA4I/aaWoaOQdWUc/s400/NewFile0.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: INV2 output&lt;br /&gt;CH2: INV1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-OGi3vQYhryk/ToAQg0YkaUI/AAAAAAAAA4Q/ZrDFVU-0adA/s1600/NewFile1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-OGi3vQYhryk/ToAQg0YkaUI/AAAAAAAAA4Q/ZrDFVU-0adA/s400/NewFile1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;CH2: INV1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Yb-c448btXI/ToAQoXveOxI/AAAAAAAAA4Y/K1CWM000kKU/s1600/NewFile2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-Yb-c448btXI/ToAQoXveOxI/AAAAAAAAA4Y/K1CWM000kKU/s400/NewFile2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I increased the capacitances as follows &lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; = 18pF&lt;br /&gt;C&lt;sub&gt;b&lt;/sub&gt; = 100pF&lt;br /&gt;and I was pleasantly surprised to see that the waveforms became much more stable--it practically ceased "wiggling" (the frequency was stable but the duty cycle wasn't and so the falling edge would be shifting rapidly to and fro horizontally--I was triggering on the rising edge so that wasn't moving at all)&lt;br /&gt;&lt;br /&gt;CH1: INV2 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Icvb1nCBiwE/ToAREW06lYI/AAAAAAAAA4g/kEECh-pd1Qs/s1600/NewFile3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-Icvb1nCBiwE/ToAREW06lYI/AAAAAAAAA4g/kEECh-pd1Qs/s400/NewFile3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Notice how the increasing the C&lt;sub&gt;b&lt;/sub&gt; to C&lt;sub&gt;a&lt;/sub&gt; ratio has decreased the signal's amplitude at C&lt;sub&gt;b&lt;/sub&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;B. XTAL = 4.000 MHz&lt;/b&gt;&lt;br /&gt;R&lt;sub&gt;f&lt;/sub&gt; = 5.1Mohm&lt;br /&gt;Rs = 2kohm&lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; = C&lt;sub&gt;b&lt;/sub&gt; = 18pF&lt;br /&gt;&lt;br /&gt;CH1: INV2 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-OCO2Rl6s6-8/ToAR4aVzKMI/AAAAAAAAA4o/Zdo2_XWEJW4/s1600/NewFile4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-OCO2Rl6s6-8/ToAR4aVzKMI/AAAAAAAAA4o/Zdo2_XWEJW4/s400/NewFile4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The CMOS 4000 series is relatively slow. In the reading above rise time is almost 37ns. &lt;br /&gt;&lt;br /&gt;CH1: INV2 output&lt;br /&gt;CH2: INV1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-4gRrKyZQ-CQ/ToAR-x3kI6I/AAAAAAAAA4w/AM3ftH3Smjw/s1600/NewFile5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-4gRrKyZQ-CQ/ToAR-x3kI6I/AAAAAAAAA4w/AM3ftH3Smjw/s400/NewFile5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;CH2: INV1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-TLFvbjfyIXU/ToASFD4N5sI/AAAAAAAAA44/zJd4vN0-7Y4/s1600/NewFile6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-TLFvbjfyIXU/ToASFD4N5sI/AAAAAAAAA44/zJd4vN0-7Y4/s400/NewFile6.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;II. National Semiconductor &lt;a href="http://www.datasheetcatalog.org/datasheet/nationalsemiconductor/DS005294.PDF"&gt;MM74HC02N&lt;/a&gt; quad 2-input NOR gate&lt;/b&gt;. Unfortunately, I don't currently have any CMOS HC or AC series inverters.&amp;nbsp; According to the datasheet each NOR gate is buffered as follows: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-dbIw0OuUz6w/Tn83ZIG_-KI/AAAAAAAAA4A/QA6xoOLXZSw/s1600/MM74HC02%2Bschematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="61" src="http://1.bp.blogspot.com/-dbIw0OuUz6w/Tn83ZIG_-KI/AAAAAAAAA4A/QA6xoOLXZSw/s400/MM74HC02%2Bschematic.png" width="354" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Using a buffered gate in a Pierce oscillator consumes less power and has a gain in the order of thousands compared to an unbuffered gate which has a gain of hundreds. The drawback of a buffered gate is that it is more sensitive to the values of the passive components and tends to be less stable. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-mu4r21NVUt8/Tn8unCJzvaI/AAAAAAAAA3w/GBb7jWPKmbw/s1600/pierce-osc-cmos-2NOR.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="253" src="http://1.bp.blogspot.com/-mu4r21NVUt8/Tn8unCJzvaI/AAAAAAAAA3w/GBb7jWPKmbw/s320/pierce-osc-cmos-2NOR.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A. XTAL = 32.768 kHz&lt;/b&gt;&lt;br /&gt;R&lt;sub&gt;f&lt;/sub&gt; = 10Mohm&lt;br /&gt;Rs = 51kohm&lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; = 33pF&lt;br /&gt;C&lt;sub&gt;b&lt;/sub&gt; = 100pF&lt;br /&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-B1vnmu4bHhI/ToAUpudMEPI/AAAAAAAAA5w/eu35GPke_ZU/s1600/NewFile13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-B1vnmu4bHhI/ToAUpudMEPI/AAAAAAAAA5w/eu35GPke_ZU/s400/NewFile13.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I believe the over- and undershoots (at the rising and falling edges) is confusing the scope. In the screenshot above it's 40.98kHz, but in real time it's all over the place, sometimes reaching as high as 80kHz. So I turned on the cursors. As you can see it says 32.89kHz. Resolution is such that moving the cursor just one pixel down results in a reading of 32.68kHz.&lt;br /&gt;&lt;br /&gt;To obtain a slightly better reading I sent the INV2 output to a &lt;a href="http://www.testmart.com/sp.cfm/FUNARB/LGP/FG-2002C/1.html"&gt;Goldstar FG-2002C&lt;/a&gt; function generator / frequency counter. The LED display has a resolution to two decimal places and said the signal was 32.77kHz. I guess the output is pretty close to the crystal's fundamental frequency. The frequency counter apparently loads the INV1 output because connecting it directly produces garbage readings (bounces around from 40 to 65kHz). A minimum of 15 ohms of resistance or a few picofarads (the smallest I have right now is 18pF) of capacitance in series is enough to satisfy INV1. The reading is the same as probing INV2 output. &lt;br /&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: NOR1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-3MGGeyIJA6A/ToAUtzTXhhI/AAAAAAAAA54/Gd9KliyFn2U/s1600/NewFile14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-3MGGeyIJA6A/ToAUtzTXhhI/AAAAAAAAA54/Gd9KliyFn2U/s400/NewFile14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: V&lt;sub&gt;DD&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-zspLAxe3wjA/ToAU316Gn3I/AAAAAAAAA6I/WEfpawWJ8hI/s1600/NewFile16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-zspLAxe3wjA/ToAU316Gn3I/AAAAAAAAA6I/WEfpawWJ8hI/s400/NewFile16.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: NOR1 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-QX48rtb9cNA/ToAU7zl3erI/AAAAAAAAA6Q/8noeTiNJ2qw/s1600/NewFile17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-QX48rtb9cNA/ToAU7zl3erI/AAAAAAAAA6Q/8noeTiNJ2qw/s400/NewFile17.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Changing the time base and capturing those overshoots/undershoots:&lt;br /&gt;&lt;br /&gt;CH1: NOR1 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;Trigger: rising edge, 1.0V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ZuG13KxS0cs/ToAVA0Ml6UI/AAAAAAAAA6Y/gG7q2I0E4hg/s1600/NewFile18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-ZuG13KxS0cs/ToAVA0Ml6UI/AAAAAAAAA6Y/gG7q2I0E4hg/s400/NewFile18.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: NOR1 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;Trigger: falling edge, 4.0V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-LXLWFqXaX7E/ToAVFbyldqI/AAAAAAAAA6g/zJMo9X3aor8/s1600/NewFile19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-LXLWFqXaX7E/ToAVFbyldqI/AAAAAAAAA6g/zJMo9X3aor8/s400/NewFile19.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;B. XTAL = 4.000 MHz&lt;/b&gt;&lt;br /&gt;R&lt;sub&gt;f&lt;/sub&gt; = 5.1Mohm&lt;br /&gt;Rs = 10kohm&lt;br /&gt;C&lt;sub&gt;a&lt;/sub&gt; = C&lt;sub&gt;b&lt;/sub&gt; = 33pF&lt;br /&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: C&lt;sub&gt;b&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-js7r9PQUrRM/ToAShIl7fZI/AAAAAAAAA5A/JzZIRuDAKb0/s1600/NewFile7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-js7r9PQUrRM/ToAShIl7fZI/AAAAAAAAA5A/JzZIRuDAKb0/s400/NewFile7.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: NOR1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-5zZEfiEqlN8/ToASrUoJfOI/AAAAAAAAA5I/l-ZWoRd27wk/s1600/NewFile8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-5zZEfiEqlN8/ToASrUoJfOI/AAAAAAAAA5I/l-ZWoRd27wk/s400/NewFile8.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: C&lt;sub&gt;b&lt;/sub&gt;&lt;br /&gt;CH2: NOR1 output&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-bATJ4HtQfL8/ToAS0odHQSI/AAAAAAAAA5Q/BTrP7wab3kk/s1600/NewFile9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-bATJ4HtQfL8/ToAS0odHQSI/AAAAAAAAA5Q/BTrP7wab3kk/s400/NewFile9.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It turns out that the scope probe might be (largely) responsible for the ringing in the output. When not probing either INV1 or INV2, V&lt;sub&gt;DD &lt;/sub&gt;has much less ripple and the ripple in C&lt;sub&gt;b&lt;/sub&gt; signal practically disappears:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CH1: C&lt;sub&gt;b&lt;/sub&gt;&lt;br /&gt;CH2: ground&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-QNSGy_MFVTs/ToATRv1xKSI/AAAAAAAAA5Y/xPX_aKxfTX8/s1600/NewFile10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-QNSGy_MFVTs/ToATRv1xKSI/AAAAAAAAA5Y/xPX_aKxfTX8/s400/NewFile10.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: NOR2 output&lt;br /&gt;CH2: V&lt;sub&gt;DD&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-r4mXFtTSGas/ToATYlgzsRI/AAAAAAAAA5g/UgdwW8Zj2dU/s1600/NewFile11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-r4mXFtTSGas/ToATYlgzsRI/AAAAAAAAA5g/UgdwW8Zj2dU/s400/NewFile11.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CH1: C&lt;sub&gt;b&lt;/sub&gt;&lt;br /&gt;CH2: V&lt;sub&gt;DD&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-IC9qVVpSrBk/ToATfeub0yI/AAAAAAAAA5o/m16QEsuBUM4/s1600/NewFile12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-IC9qVVpSrBk/ToATfeub0yI/AAAAAAAAA5o/m16QEsuBUM4/s400/NewFile12.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;References:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Don Lancaster and Howard M. Berlin, &lt;i&gt;CMOS Cookbook&lt;/i&gt;, 2ed., Howard W. Sams, 1988, p.279-281&lt;/li&gt;&lt;li&gt;&lt;a href="http://ww1.microchip.com/downloads/en/AppNotes/00849a.pdf"&gt;AN849: Basic PICmicro® Oscillator Design&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.mpdigest.com/issue/Articles/2008/Mar/Crystek/Default.asp"&gt;Pierce-Gate Crystal Oscillator, an Introduction&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://cache.freescale.com/files/microcontrollers/doc/app_note/AN1706.pdf"&gt;AN1706/D Microcontroller Oscillator Circuit Design Considerations&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://focus.ti.com/general/docs/lit/getliterature.tsp?literatureNumber=szza043&amp;amp;fileType=pdf"&gt;SZZA043: Use of the CMOS Unbuffered Inverter in Oscillator Circuits&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.fairchildsemi.com/an/AN/AN-340.pdf"&gt;AN340: HCMOS Crystal Oscillators&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-26622438124351252?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/26622438124351252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/checking-out-pierce-oscillator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/26622438124351252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/26622438124351252'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/checking-out-pierce-oscillator.html' title='Checking out the Pierce oscillator'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-mAmLljbgTAY/Tn7WVO2MNgI/AAAAAAAAA3g/7xNOq1-c-AU/s72-c/pierce-osc-cmos-inverter.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4235946503639294233</id><published>2011-09-23T11:40:00.001+08:00</published><updated>2011-09-25T22:04:35.317+08:00</updated><title type='text'>Taking a look at basic ring oscillators</title><content type='html'>Just playing around with ring oscillators cobbled up using a number of basic CMOS gates. Measured V&lt;sub&gt;DD&lt;/sub&gt; of the linear power supply = 5.07 to 5.09V for all circuits. Circuits were assembled on a solderless breadboard. Interconnect wires varied in length. And the relatively long interconnects--compared to the short traces had these been fabricated on a PCB--almost surely affected performance and the characteristics of the oscillators. &lt;br /&gt;&lt;br /&gt;I. First up is the Fairchild &lt;a href="http://www.datasheetcatalog.org/datasheet2/5/0qkju19070ho6yap6q12xs504kfy.pdf"&gt;CD40106BCN&lt;/a&gt; hex Schmitt inverter. According to the datasheet its maximum propagation delay = 400ns at 25°C and V&lt;sub&gt;DD&lt;/sub&gt; = 5.0V. Its maximum rise/fall time (transition time) = 200ns at 25°C and V&lt;sub&gt;DD&lt;/sub&gt; = 5.0V. This oscillator has the lowest frequency among those tested. But its output is also the closest to looking like a square wave. Numbers in the schematics refer to IC pin numbers &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-bIAQxQD2Hzk/Tntd4L8yWcI/AAAAAAAAA1w/PEercTGb6Xw/s1600/ring-osc-schmitt-inv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="89" src="http://4.bp.blogspot.com/-bIAQxQD2Hzk/Tntd4L8yWcI/AAAAAAAAA1w/PEercTGb6Xw/s400/ring-osc-schmitt-inv.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I measured the output at the various pins to see how transition time would be affected--wanted to decrease it to make the wave as square as possible. The most significant effect occurs with the very first inverter after the oscillator. However, as you can see below, having inverters to "clean up" output after the oscillator has an impact on frequency as well.&lt;br /&gt;&lt;br /&gt;Output at pin 8:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ZFJplvDK9LU/TntpeuIqE4I/AAAAAAAAA14/POGz03Kk3sE/s1600/NewFile0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-ZFJplvDK9LU/TntpeuIqE4I/AAAAAAAAA14/POGz03Kk3sE/s400/NewFile0.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 2:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-D5LmwkCUKL8/TntppRB4aXI/AAAAAAAAA2A/7AbgxAGI-jY/s1600/NewFile1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-D5LmwkCUKL8/TntppRB4aXI/AAAAAAAAA2A/7AbgxAGI-jY/s400/NewFile1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 4:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-gF5usSrQgPA/TntpxM-BXAI/AAAAAAAAA2I/higqCo2TJJY/s1600/NewFile2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-gF5usSrQgPA/TntpxM-BXAI/AAAAAAAAA2I/higqCo2TJJY/s400/NewFile2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 6:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Xh1ll8G-nJA/Tntp4XPO_qI/AAAAAAAAA2Q/HFZVYyHvyZE/s1600/NewFile3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-Xh1ll8G-nJA/Tntp4XPO_qI/AAAAAAAAA2Q/HFZVYyHvyZE/s400/NewFile3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;II. Motorola &lt;a href="http://www.datasheetcatalog.org/datasheet2/a/0s6fgf8728z3o4kzqz87qk4uzpcy.pdf"&gt;MC14049UBCP&lt;/a&gt; hex inverting buffer. After discovering how lousy the output of a 3-inverter configuration was I tried a 5-inverter one. As you will see there is improvement but it isn't that remarkable, even if the frequency is almost halved. &lt;br /&gt;&lt;br /&gt;At room temperature and V&lt;sub&gt;DD&lt;/sub&gt; = 5.0V, maximum rise time of the 4049 is 160ns, while maximum fall time is 60ns. Maximum propagation delay for low to high transition = 120ns. For high to low max propagation delay = 60ns. &lt;br /&gt;&lt;br /&gt;A. Using 3 inverters:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-n5faBpTX9fk/Tntd3lBCb0I/AAAAAAAAA1Y/Hlgd5jjhlyE/s1600/ring-osc-3-inv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="92" src="http://2.bp.blogspot.com/-n5faBpTX9fk/Tntd3lBCb0I/AAAAAAAAA1Y/Hlgd5jjhlyE/s400/ring-osc-3-inv.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Output at pin 15:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ctnylSGb2X4/TntqOpYj44I/AAAAAAAAA2o/t0JbPhuQaIY/s1600/NewFile6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-ctnylSGb2X4/TntqOpYj44I/AAAAAAAAA2o/t0JbPhuQaIY/s400/NewFile6.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 6:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-YL7BQLdG_50/TntqW9-JtdI/AAAAAAAAA2w/mLnUkcMoqaE/s1600/NewFile7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-YL7BQLdG_50/TntqW9-JtdI/AAAAAAAAA2w/mLnUkcMoqaE/s400/NewFile7.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 4:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-vMzCHraFvl0/Tntqfth5nuI/AAAAAAAAA24/o0WJlJjOCkI/s1600/NewFile8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-vMzCHraFvl0/Tntqfth5nuI/AAAAAAAAA24/o0WJlJjOCkI/s400/NewFile8.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 2: &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-cYuPQ7FMuYI/Tntqm9v6ceI/AAAAAAAAA3A/fNcnhVxhisA/s1600/NewFile9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-cYuPQ7FMuYI/Tntqm9v6ceI/AAAAAAAAA3A/fNcnhVxhisA/s400/NewFile9.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;B. Using 5 inverters:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-eUIUIZSkbeA/Tntd39PLpvI/AAAAAAAAA1g/r2z1tjhEgUw/s1600/ring-osc-5-inv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="98" src="http://4.bp.blogspot.com/-eUIUIZSkbeA/Tntd39PLpvI/AAAAAAAAA1g/r2z1tjhEgUw/s400/ring-osc-5-inv.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Output pin 4:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-p9U_0SUOT_w/TntqxV7ri3I/AAAAAAAAA3I/VO0eDO_-SSw/s1600/NewFile10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-p9U_0SUOT_w/TntqxV7ri3I/AAAAAAAAA3I/VO0eDO_-SSw/s400/NewFile10.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 2: &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-HbVecF0vgCc/Tntq6WrocAI/AAAAAAAAA3Q/J_SQKaW28sc/s1600/NewFile11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-HbVecF0vgCc/Tntq6WrocAI/AAAAAAAAA3Q/J_SQKaW28sc/s400/NewFile11.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;III. If I had them in my inventory I would've tested a 74AC hex inverter and Schmitt trigger, but the only part I have that comes close is a National Semiconductor &lt;a href="http://www.datasheetcatalog.org/datasheet/nationalsemiconductor/DS005294.PDF"&gt;MM74HC02N&lt;/a&gt; quad 2-input NOR gate. Datasheet says it has a maximum propagation delay of 18ns at 25°C and V&lt;sub&gt;DD&lt;/sub&gt; = 4.5V. Maximum rise and fall time = 15ns at 25°C and V&lt;sub&gt;DD&lt;/sub&gt; = 4.5V.&amp;nbsp; With those specs, it's easy to see that the 74HC line is ultrafast compared to the jurassic 4000 series. However, its transition time to propagation delay time ratio isn't good enough to produce square waves. Actually the output is close to a sine wave. In fact, as can been seen in the scope readings, its rise and fall times aren't even short enough for the output to reach V&lt;sub&gt;DD&lt;/sub&gt; or ground. &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-2I6S4yrzYF8/Tn80tgTT1wI/AAAAAAAAA34/BSnpToaQL9Y/s1600/ring-osc-nor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="133" src="http://3.bp.blogspot.com/-2I6S4yrzYF8/Tn80tgTT1wI/AAAAAAAAA34/BSnpToaQL9Y/s400/ring-osc-nor.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;At pin 10:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-vRKpdwc7qP8/TntqBY70dII/AAAAAAAAA2Y/LbX61Up_2Uo/s1600/NewFile4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-vRKpdwc7qP8/TntqBY70dII/AAAAAAAAA2Y/LbX61Up_2Uo/s400/NewFile4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At pin 13:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-XQ0qKGhC2d4/TntqGiALviI/AAAAAAAAA2g/2e0-mWKQKUI/s1600/NewFile5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-XQ0qKGhC2d4/TntqGiALviI/AAAAAAAAA2g/2e0-mWKQKUI/s400/NewFile5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4235946503639294233?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4235946503639294233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/taking-look-at-basic-ring-oscillators.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4235946503639294233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4235946503639294233'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/taking-look-at-basic-ring-oscillators.html' title='Taking a look at basic ring oscillators'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-bIAQxQD2Hzk/Tntd4L8yWcI/AAAAAAAAA1w/PEercTGb6Xw/s72-c/ring-osc-schmitt-inv.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4806689800997884794</id><published>2011-09-16T22:52:00.000+08:00</published><updated>2011-09-16T22:52:49.408+08:00</updated><title type='text'>A very noisy table lamp</title><content type='html'>Using the &lt;a href="http://electromotiveforces.blogspot.com/2009/12/atx-in-ats-clothing.html"&gt;ATX switching power supply&lt;/a&gt; I was testing a circuit consisting of a PIC12F1822 and SN75176 transceiver, plus a couple of LEDs. The circuit has 10uF filter caps and the MCU has a 0.1uF bypass cap. Much to my chagrin the fluorescent lamp on my bench was affecting the circuit--the MCU  would sometimes do a power-on reset when I turned the lamp on or off. The desk lamp has an electronic ballast/starting circuit and has an 18-watt circular fluorescent tube. Adding more caps--&lt;span class="fbPhotoCaptionText"&gt;100pF, 1nF, 10nF, 100uF--were for naught.&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;To find out how what the heck was happening I hooked up the oscilloscope to the power rails. I was horrified by what I saw. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Xih2zj_E298/TnNWbzMqRZI/AAAAAAAAAxM/cn__9sosmZA/s1600/NewFile3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-Xih2zj_E298/TnNWbzMqRZI/AAAAAAAAAxM/cn__9sosmZA/s400/NewFile3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-NcEOw28YYtU/TnNWyaqdMmI/AAAAAAAAAxc/SiwD-Y3J400/s1600/NewFile4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-NcEOw28YYtU/TnNWyaqdMmI/AAAAAAAAAxc/SiwD-Y3J400/s400/NewFile4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-rE15_3qY5wU/TnNW3v85gRI/AAAAAAAAAxs/K7f8zXfkCUE/s1600/NewFile6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-rE15_3qY5wU/TnNW3v85gRI/AAAAAAAAAxs/K7f8zXfkCUE/s400/NewFile6.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-TC3Jcv7a5x4/TnNW3pxqxjI/AAAAAAAAAxk/ZhSKIMuE_v4/s1600/NewFile5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-TC3Jcv7a5x4/TnNW3pxqxjI/AAAAAAAAAxk/ZhSKIMuE_v4/s400/NewFile5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-6yTjS2bxxMM/TnNW9MPqzVI/AAAAAAAAAx0/zyTerIHorNI/s1600/NewFile7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-6yTjS2bxxMM/TnNW9MPqzVI/AAAAAAAAAx0/zyTerIHorNI/s400/NewFile7.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The transient would sometimes reach 20V peak to peak! Fortunately it would last for less than 20us. To determine the frequency, I turned on the FFT. As you can see below the range is between 20 to 100Mhz.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-uzkjwS0XZhY/TnNXNA56w5I/AAAAAAAAAx8/xpC5GqGKjPo/s1600/NewFile13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-uzkjwS0XZhY/TnNXNA56w5I/AAAAAAAAAx8/xpC5GqGKjPo/s400/NewFile13.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-6p-5vfR1bwk/TnNXNIaGsRI/AAAAAAAAAyE/XD9gMoIrhRM/s1600/NewFile14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-6p-5vfR1bwk/TnNXNIaGsRI/AAAAAAAAAyE/XD9gMoIrhRM/s400/NewFile14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-kCplJSpkJBA/TnNXNQbgAvI/AAAAAAAAAyM/_ukBos1Ca1c/s1600/NewFile16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-kCplJSpkJBA/TnNXNQbgAvI/AAAAAAAAAyM/_ukBos1Ca1c/s400/NewFile16.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-rlRGL0uIwSY/TnNXeHsXidI/AAAAAAAAAyU/t7W-xYsgbJA/s1600/NewFile17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-rlRGL0uIwSY/TnNXeHsXidI/AAAAAAAAAyU/t7W-xYsgbJA/s400/NewFile17.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-KMJPpLy0GrA/TnNXeKb-AEI/AAAAAAAAAyc/m2GAiU3Z2Fs/s1600/NewFile18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-KMJPpLy0GrA/TnNXeKb-AEI/AAAAAAAAAyc/m2GAiU3Z2Fs/s400/NewFile18.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-QTAkG2rz1ic/TnNXefXlaDI/AAAAAAAAAyk/C0Q86_IYmik/s1600/NewFile19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-QTAkG2rz1ic/TnNXefXlaDI/AAAAAAAAAyk/C0Q86_IYmik/s400/NewFile19.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I wondered whether a linear power supply would be immune to the transients. I hastily cobbled up one using a 220VAC to 12VAC transformer, W01M bridge rectifier, 470uF filter cap, 78L05 5VDC voltage regulator and a 200-ohm resistor as load. Here are scope readings:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-u6_gHnUf_HQ/TnNZOZEcXKI/AAAAAAAAAys/7ctFcdgyTXY/s1600/linearsupply0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-u6_gHnUf_HQ/TnNZOZEcXKI/AAAAAAAAAys/7ctFcdgyTXY/s400/linearsupply0.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Mp8OnMCz7U0/TnNZOmTRnII/AAAAAAAAAy0/U1-TgbRcqjU/s1600/linearsupply1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-Mp8OnMCz7U0/TnNZOmTRnII/AAAAAAAAAy0/U1-TgbRcqjU/s400/linearsupply1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-EwCcg5Hu7Mk/TnNZOiqSuLI/AAAAAAAAAy8/JxY3ptkFnYk/s1600/linearsupply2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-EwCcg5Hu7Mk/TnNZOiqSuLI/AAAAAAAAAy8/JxY3ptkFnYk/s400/linearsupply2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-P7ShQcsRn_4/TnNZO402muI/AAAAAAAAAzE/jrY5mK0fPdQ/s1600/linearsupply3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-P7ShQcsRn_4/TnNZO402muI/AAAAAAAAAzE/jrY5mK0fPdQ/s400/linearsupply3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-mykXW9OfbZc/TnNZPE0Zi9I/AAAAAAAAAzM/7I9FEOfpNsU/s1600/linearsupply4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-mykXW9OfbZc/TnNZPE0Zi9I/AAAAAAAAAzM/7I9FEOfpNsU/s400/linearsupply4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-I6LAKXsdF_8/TnNZYKawamI/AAAAAAAAAzU/qeQeD59UAeE/s1600/linearsupply5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-I6LAKXsdF_8/TnNZYKawamI/AAAAAAAAAzU/qeQeD59UAeE/s400/linearsupply5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Most of the noise is in the range &amp;lt;40Mhz. The amplitude of the transients seem less than for the ATX supply. However, this supply wasn't connected to the MCU circuit. That may have had an effect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4806689800997884794?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4806689800997884794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/very-noisy-table-lamp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4806689800997884794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4806689800997884794'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/very-noisy-table-lamp.html' title='A very noisy table lamp'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-Xih2zj_E298/TnNWbzMqRZI/AAAAAAAAAxM/cn__9sosmZA/s72-c/NewFile3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-3854595880248620952</id><published>2011-09-09T12:00:00.002+08:00</published><updated>2011-09-10T17:54:12.912+08:00</updated><title type='text'>MCU linear regression implementation</title><content type='html'>Due to noise in the circuit and real-time fluctuations in the phenomenon being measured the readings from a sensor are unsteady and would jump around from millisecond to millisecond just like the day to day up and down variations in the index of trading bourses. However, in the long run the value does show a trend as the variable being measured increases or decreases or remains relatively steady. Thus I'm trying out linear regression--fitting a line through the mess of data points--to tease out this trend. I've used the &lt;a href="http://www.westgard.com/lesson43.htm"&gt;least squares method&lt;/a&gt; since that's what I'm familiar with (actually it's been years and had to hit the math textbooks to refresh my memory!).&lt;br /&gt;&lt;br /&gt;What I need to know during every sensor read is the trend at that point. But of course I need some historical readings to compute for it. So I take the last n readings and use those to derive the line of best fit through the n data points. I only need to know the slope of the line (the y-intercept, i.e., the b in ax+b, is irrelevant). If the slope is positive then this implies the value being sensed is increasing, if it's negative it's decreasing, and if it's very close to zero then it's plateaued. &lt;br /&gt;&lt;br /&gt;In the application that I have sensor readings are taken at fixed time  intervals. The last n sensor readings are stored in an array. During every sensor read the slope is calculated. Because we're always taking the last n readings every time we compute for the slope what we're doing is analogous to computing for a moving average. In this case we can call it a moving linear regression.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The slope of the line of best fit is given by&lt;br /&gt;&lt;br /&gt;(nΣxy - ΣxΣy) / [nΣx&lt;sup&gt;2&lt;/sup&gt; - (Σx)&lt;sup&gt;2&lt;/sup&gt;]&lt;br /&gt;where&lt;br /&gt;&lt;br /&gt;x = point in time&lt;br /&gt;y = sensor reading&lt;br /&gt;&lt;br /&gt;y is easy enough to understand. That's just the ADC reading of the analog sensor output. x, as I'm using it here, needs a little more clarification. Let's say our oldest reading of n readings is in cell y[1] and the latest reading is in y[n]. y[i] was taken at time x[i] . But x[i] does not store actual clock time, but rather normalized time. Thus x[1] = 1, x[2] = 2, ...., x[n] = n. The values of x are fixed even as we discard the old y value and shift in the latest. Moreover, the actual time interval between reads isn't or need not be reflected in x as well. Thus the actual elapsed time between two readings may be 5ms, 48ms, 100ms, 2000ms or what have you. As long as that time interval is fixed for all readings then we're good. &lt;br /&gt;&lt;br /&gt;Because the values of x never change, the above equation can be greatly simplified. The formula for an arithmetic progression Σx = n(n+1)/2. Hence the least squares equation can be rewritten as&lt;br /&gt;&lt;br /&gt;[nΣxy - n(n+1)(Σy)/2] / [nΣx&lt;sup&gt;2&lt;/sup&gt; - n&lt;sup&gt;2&lt;/sup&gt;(n+1)&lt;sup&gt;2&lt;/sup&gt;/4]&lt;br /&gt;&lt;br /&gt;n is common to both the numerator and denominator and so cancel out. We're left with&lt;br /&gt;&lt;br /&gt;[Σxy - (n+1)(Σy)/2] / [Σx&lt;sup&gt;2&lt;/sup&gt; - n&lt;sup&gt;&lt;/sup&gt;(n+1)&lt;sup&gt;2&lt;/sup&gt;/4]&lt;br /&gt;&lt;br /&gt;At this point we can provide the firmware the value of n and let it do the calculations, but I'm using an MCU with only 2K of flash. And because I don't want to slow the processor down with unnecessary math computations--whose answers we already know beforehand because n is specified in the defines--I've simplified the formula further still. &lt;br /&gt;&lt;br /&gt;Because the 8-bit PICs have very limited RAM and since the rest of the  firmware also needs a lot of memory, I settled on n = 15 (I initially tried n = 31 but ran out of RAM). This number is based on the criterion n = 2&lt;sup&gt;i&lt;/sup&gt;- 1, where i = integer, so that n+1 = 2&lt;sup&gt;i&lt;/sup&gt;. The rationale for this constraint is that multiplying by 2&lt;sup&gt;i&lt;/sup&gt; requires nothing more than left shifting the register bits. This reduces computing time and may also reduce code length. With n = 15 the slope of the line simplifies to&lt;br /&gt;&lt;br /&gt;[Σxy - (15+1)(Σy)/2] / [Σx&lt;sup&gt;2&lt;/sup&gt; - 15(16)&lt;sup&gt;2&lt;/sup&gt;/4]&lt;br /&gt;&lt;br /&gt;[Σxy - 8(Σy)] / [Σx&lt;sup&gt;2&lt;/sup&gt; - 960]&lt;br /&gt;&lt;br /&gt;Because x = 1, 2, ... n, Σx&lt;sup&gt;2&lt;/sup&gt; = 1 + 4 + 9 + .... + 225 = 1,240. So the above further simplifies to&lt;br /&gt;&lt;br /&gt;[Σxy - 8(Σy)] / [1,240 - 960]&lt;br /&gt;&lt;br /&gt;(Σxy - 8Σy) / 280&lt;br /&gt;&lt;br /&gt;Keep in mind the above equation is valid only for n = 15. For other values of n the constants will be different.&lt;br /&gt;&lt;br /&gt;Here a C implementation of the above equation. It's in mikroC v.5.0.0. Because there are type conversions--integer to floating point and back--it's important to get type casting correctly or you'll get garbage. If in doubt cast the variables explicitly.&lt;br /&gt;&lt;br /&gt;Given n = 15, the latest sensor reading is stored in Y[15]. The 15 historical readings are in Y[0] to Y[14]. These 15 values are the ones used in the equation. Y[15] will be copied into Y[14] and used in the next cycle through the state machine (not shown below). Because I want to see the value of the slope, I've added&amp;nbsp; variable INTSLOPE. To reduce complications of deriving this from the floating point variable SLOPE when it's negative, the firmware computes for (Σxy - 8Σy) such that it's always positive. Because the slope can be and may often lie between -1 and +1, SLOPE is multiplied by 1000 before it's converted to an integer type and stored in INTSLOPE. This provides a resolution of 3 decimal places. For instance if INTSLOPE = 3571 then the actual slope = 3571/1000 = 3.571. The 16-bit INTSLOPE and the sign (plus or minus) of the slope are then transmitted out of the EUSART TX pin so I can view the values on a logic analyzer. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;// MCU = PIC12F18xx or PIC16F18xx&lt;br /&gt;// compiler = mikroC v5.0.0&lt;br /&gt;// September 2011&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define  n                   15        // number of data points for linear regression&lt;br /&gt;#define  positive            1         // sign of slope&lt;br /&gt;#define  negative            0         // sign of slope&lt;br /&gt;&lt;br /&gt;void LinReg()&lt;br /&gt;{&lt;br /&gt;  unsigned char i;           // counter&lt;br /&gt;  unsigned int Y[n+1];       // newest reading is temporarily stored in Y[n]&lt;br /&gt;                             // and then copied to Y[n-1]&lt;br /&gt;                             // oldest reading is in Y[0] &lt;br /&gt;  unsigned int SUMY;         // sum of all Y&lt;br /&gt;  unsigned int SUMXY;        // sum of all X*Y&lt;br /&gt;  bit   sign;                // 1 = slope is positive; 0 = slope is negative&lt;br /&gt;  float SLOPE;               // linear regression value of the slope&lt;br /&gt;  unsigned int INTSLOPE;     // integer version of SLOPE. Actually equal to 1000*SLOPE&lt;br /&gt;&lt;br /&gt;  Y[n] = CURR;               // CURR = latest sensor reading&lt;br /&gt;  SUMXY = SUMY = 0;&lt;br /&gt;  for (i=0; i&amp;lt;n; i++)&lt;br /&gt;  {&lt;br /&gt;    SUMXY += Y[i]*(i+1);&lt;br /&gt;    SUMY += Y[i];&lt;br /&gt;    Y[i] = Y[i+1];           // discard oldest reading and left-shift all readings including latest one&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  SUMY *= 8;&lt;br /&gt;  if (SUMXY &amp;gt;= SUMY)&lt;br /&gt;  {&lt;br /&gt;    SUMXY -= SUMY;&lt;br /&gt;    sign = positive;&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    SUMXY = SUMY - SUMXY;&lt;br /&gt;    sign = negative;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  SLOPE = SUMXY/280.0;       // this particular equation is valid only for n = 15&lt;br /&gt;                             // SLOPE will always be positive.&lt;br /&gt;                             // Check variable "sign" to determine if slope is negative or positive&lt;br /&gt;  INTSLOPE = SLOPE*1000.0;   &lt;br /&gt;  &lt;br /&gt;  // transmit 16-bit slope and its sign via EUSART&lt;br /&gt;  // EUSART is set up in a separate initialization routine&lt;br /&gt;  while (!PIR1.TXIF);&lt;br /&gt;  TXREG = sign;              // sign byte; 1 = positive slope, 0 = negative slope&lt;br /&gt;&lt;br /&gt;  asm {nop}                  // datasheet says TXIF will give false value if read right after loading TXREG&lt;br /&gt;  while (!PIR1.TXIF);&lt;br /&gt;  TXREG = INTSLOPE &amp;gt;&amp;gt; 8;     // high byte&lt;br /&gt;&lt;br /&gt;  asm {nop}                  // datasheet says TXIF will give false value if read right after loading TXREG&lt;br /&gt;  while (!PIR1.TXIF);&lt;br /&gt;  TXREG = INTSLOPE;          // low byte&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IMPORTANT ERRATA&lt;/b&gt;&lt;br /&gt;September 10 2011&lt;br /&gt;&lt;br /&gt;I was fiddling with the circuit and found the following needed to be changed: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;unsigned int SUMY;         // sum of all Y&lt;br /&gt;unsigned int SUMXY;        // sum of all X*Y&lt;/pre&gt;&lt;br /&gt;to &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;unsigned long SUMY;         // sum of all Y&lt;br /&gt;unsigned long SUMXY;        // sum of all X*Y&lt;/pre&gt;&lt;br /&gt;16 bits isn't enough when using a 10-bit ADC. &lt;code&gt;SUMXY&lt;/code&gt; can exceed 0xFFFF. While Σy will not exceed 16 bits, 8Σy may. So to make sure, I changed both to 32-bit integers.&lt;br /&gt;&lt;br /&gt;For a 10-bit ADC, each &lt;code&gt;Y[i]&lt;/code&gt; will not exceed 16 bits of course. However, I found out after hours of going nuts that mikroC does not like the following expression/assignment &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SUMXY += Y[i]*(i+1);&lt;/pre&gt;&lt;br /&gt;because, as far as I can tell, it goes bonkers due to the fact that &lt;code&gt;SUMXY&lt;/code&gt; is 32 bits while &lt;code&gt;Y[i]&lt;/code&gt; is 16 bits and &lt;code&gt;i&lt;/code&gt; is 8 bits. To prevent &lt;code&gt;SUMXY&lt;/code&gt; from ending up with garbage I had to change the above to &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SUMXY = SUMXY + Y[i]*(i+1);&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-3854595880248620952?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/3854595880248620952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/mcu-linear-regression-implementation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3854595880248620952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3854595880248620952'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/mcu-linear-regression-implementation.html' title='MCU linear regression implementation'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-1066879863011866919</id><published>2011-09-02T15:04:00.001+08:00</published><updated>2011-09-02T15:12:22.791+08:00</updated><title type='text'>Gear trivia for not so trivial protection</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-fD3oFxwl9Q8/TmB-CgDS2VI/AAAAAAAAAws/zCmBRBE858s/s1600/insulated-probes.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-fD3oFxwl9Q8/TmB-CgDS2VI/AAAAAAAAAws/zCmBRBE858s/s320/insulated-probes.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;1. All the DMM probes I own have standard exposed metal rods around 2cm long. When doing hot checks on boards or high voltage AC what-have-you I always have to beware of shorting connections and even the probes and such booboos. To minimize inadvertent and unwanted contacts which could have disastrous consequences, I insulated one set of probes with heat shrink tubing, leaving only the conical tip bare. Now they're ultra safe to use. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-GTfNOaac1O4/TmB-LU-UtrI/AAAAAAAAAw0/aLKfQoikHOI/s1600/wrist-strap.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="208" src="http://4.bp.blogspot.com/-GTfNOaac1O4/TmB-LU-UtrI/AAAAAAAAAw0/aLKfQoikHOI/s320/wrist-strap.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;2. I don't have a purchased ESD wrist strap. I figure I don't need one if the principle is to ground me to earth potential. What I've been using for years now is this. I have a wire that's connected to a copper stake that's been driven into the soil (earth). A 1 Mohm resistor is soldered in series. This resistor limits the current that may possibly go through you know who. At the other end of the wire is an alligator clip. Since I don't particularly like gators biting me, I wear my watch which has a metal strap to which I clip the alligator onto. When not using it on my wrist, the alligator clips onto a metal sheet I have on the bench. When I can't find my watch or am just too lazy to even put it on, I touch and wipe my hands on the sheet every so often and hope to Thor I won't zap any ICs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-1066879863011866919?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/1066879863011866919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/gear-trivia-for-not-so-trivial.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1066879863011866919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1066879863011866919'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/09/gear-trivia-for-not-so-trivial.html' title='Gear trivia for not so trivial protection'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-fD3oFxwl9Q8/TmB-CgDS2VI/AAAAAAAAAws/zCmBRBE858s/s72-c/insulated-probes.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-6459944620139278748</id><published>2011-08-30T14:42:00.001+08:00</published><updated>2011-08-30T14:44:01.987+08:00</updated><title type='text'>Taking a peek at RS-485 signals</title><content type='html'>Did a test of RS-485 lines. See the &lt;a href="http://electromotiveforces.blogspot.com/2011/08/using-rs-485-transceivers.html"&gt;schematic&lt;/a&gt; for the transmitter section of the circuit. Three SN75176B transceivers were employed. One was configured as driver (transmitter),  two as receivers. Driver was on only when transmitting. Vcc was cut off  when not transmitting. Transmitter and receivers were connected using Samsung 4-pair AWG24 Category 5 UTP cable. Cable length from transmitter to the farthest receiver was around 30 meters. One pair was used for RS-485 line A and B, two pairs for power supply to the transmitter. The test receiver used below had its own power supply but all transceivers shared a common ground. There were no termination resistors across lines A and B. Pullup and pulldown resistors were as per schematic--20kohms each. Data packet consisted of 4 bytes + a 9th bit. Minimum time interval between packet transmission was ~50ms. Baud rate was 19.2 kbps. &lt;br /&gt;&lt;br /&gt;In the following screenshots, &lt;br /&gt;Channel 1 (yellow) = Line A&lt;br /&gt;Channel 2 (cyan) = Line B&lt;br /&gt;Math channel (purple) = A - B&lt;br /&gt;&lt;br /&gt;Overshoots and undershoots are clearly visible during high/low transitions. In all probability these are due to signal reflections in the cable. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-vwYGf3pQYK8/TlusilQ5Q7I/AAAAAAAAAuc/QL7pNlgw0Ug/s1600/SN75176B%2B1.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-vwYGf3pQYK8/TlusilQ5Q7I/AAAAAAAAAuc/QL7pNlgw0Ug/s400/SN75176B%2B1.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Zooming in:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-0yefciRUdoo/Tlus0ZxnJqI/AAAAAAAAAu8/HNF7RvUhFTU/s1600/SN75176B%2B2.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-0yefciRUdoo/Tlus0ZxnJqI/AAAAAAAAAu8/HNF7RvUhFTU/s400/SN75176B%2B2.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;With math channel turned on: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-b32cAxCcpY8/Tlus0VqDt8I/AAAAAAAAAu0/6Esq9ySqR9g/s1600/SN75176B%2B3.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-b32cAxCcpY8/Tlus0VqDt8I/AAAAAAAAAu0/6Esq9ySqR9g/s400/SN75176B%2B3.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Channels 1 and 2 are turned off and vertical scale of math is decreased from 5 to 2V/division: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Domagk8awoI/Tlus0EpMOmI/AAAAAAAAAus/7t1u6qhmf3M/s1600/SN75176B%2B4.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-Domagk8awoI/Tlus0EpMOmI/AAAAAAAAAus/7t1u6qhmf3M/s400/SN75176B%2B4.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Vertical scale reduced to1V/div. Look at the left half of the trace. When transmitter is off A - B is approximately 1.0V. (Adjusting the vertical scale to &amp;lt;1V/div and using cursors, I was able to take a more accurate reading of 1.08V. With only one receiver connected A-B = 1.50V). Since this is &amp;gt;200mV pin R of the receivers will be high--the mandatory and expected line idle state. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-95YWe2lPuw4/TluszyOBWzI/AAAAAAAAAuk/IhiBg612iQU/s1600/SN75176B%2B5.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-95YWe2lPuw4/TluszyOBWzI/AAAAAAAAAuk/IhiBg612iQU/s400/SN75176B%2B5.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Here are zoomed in views of over- and undershoots. The ringing dies out well before half a bit width. The first screenshot shows line A going from low to high. The second shows line A going from high to low. Note the different horizontal scales--500ns in the first, 200ns in the second. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-0obMckW352Q/Tlx0tKhnmkI/AAAAAAAAAvE/89K-WAnkLd0/s1600/SN75176B%2B6.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://2.bp.blogspot.com/-0obMckW352Q/Tlx0tKhnmkI/AAAAAAAAAvE/89K-WAnkLd0/s400/SN75176B%2B6.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-sZdbLGMRFww/Tlx1CUK_HDI/AAAAAAAAAvM/UMmL_WPnNeg/s1600/SN75176B%2B7.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-sZdbLGMRFww/Tlx1CUK_HDI/AAAAAAAAAvM/UMmL_WPnNeg/s400/SN75176B%2B7.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;My Rigol scope only has two channels so in the following screenshots&lt;br /&gt;Channel 1: Line A&lt;br /&gt;Channel 2: SN75176B pin R&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-sgW8CO00udY/Tlx1tKa0QFI/AAAAAAAAAvU/DLoUtpIyPKk/s1600/SN75176B%2B8.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-sgW8CO00udY/Tlx1tKa0QFI/AAAAAAAAAvU/DLoUtpIyPKk/s400/SN75176B%2B8.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Even with the ringing there are no errors in the output of the receiver, although undershoot does occur occasionally in pin R output (the Rigol is prone to aliasing, so there may in fact be very high frequency ringing which isn't showing up at this horizontal scale.)&lt;br /&gt;&lt;br /&gt;With horizontal scale reduced, we get to see details of undershoots on pin R.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-mE0_ACZ-Fl4/Tlx3vOw0TNI/AAAAAAAAAvk/0aYGvALuJtY/s1600/SN75176B%2B12.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-mE0_ACZ-Fl4/Tlx3vOw0TNI/AAAAAAAAAvk/0aYGvALuJtY/s400/SN75176B%2B12.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-E9jrVJ5wRI4/Tlx5RqCGA7I/AAAAAAAAAv8/R1cxBPZZFGg/s1600/SN75176B%2B13.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-E9jrVJ5wRI4/Tlx5RqCGA7I/AAAAAAAAAv8/R1cxBPZZFGg/s400/SN75176B%2B13.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-qTmhSqk1JFc/Tlx3u2qLO3I/AAAAAAAAAvc/4gbkGmwAEXg/s1600/SN75176B%2B9.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-qTmhSqk1JFc/Tlx3u2qLO3I/AAAAAAAAAvc/4gbkGmwAEXg/s400/SN75176B%2B9.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Overshoots when pin R goes from low to high are probably less frequent--I haven't been able to capture any yet. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ag5ZXCsusnc/Tlx5HHEOXgI/AAAAAAAAAv0/njKuWkCBctE/s1600/SN75176B%2B11.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-ag5ZXCsusnc/Tlx5HHEOXgI/AAAAAAAAAv0/njKuWkCBctE/s400/SN75176B%2B11.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Just to be fair to line B I let&amp;nbsp; &lt;br /&gt;Channel 1: Line B&lt;br /&gt;Channel 2: SN75176B pin R&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-MhoqmZevOL0/Tlx6xVaxsYI/AAAAAAAAAwE/S8CEzvazPes/s1600/SN75176B%2B15.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://1.bp.blogspot.com/-MhoqmZevOL0/Tlx6xVaxsYI/AAAAAAAAAwE/S8CEzvazPes/s400/SN75176B%2B15.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-NXO9tidqLQs/Tlx6xm53pzI/AAAAAAAAAwM/JYHq6zh69xQ/s1600/SN75176B%2B16.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-NXO9tidqLQs/Tlx6xm53pzI/AAAAAAAAAwM/JYHq6zh69xQ/s400/SN75176B%2B16.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-tUdU3Dh63Ms/Tlx6x7sCdXI/AAAAAAAAAwU/r8i9i2BmwgE/s1600/SN75176B%2B17.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://4.bp.blogspot.com/-tUdU3Dh63Ms/Tlx6x7sCdXI/AAAAAAAAAwU/r8i9i2BmwgE/s400/SN75176B%2B17.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-6pKjL7n1SUs/Tlx6xwqZjDI/AAAAAAAAAwc/n4O2npRbvc0/s1600/SN75176B%2B18.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="292" src="http://3.bp.blogspot.com/-6pKjL7n1SUs/Tlx6xwqZjDI/AAAAAAAAAwc/n4O2npRbvc0/s400/SN75176B%2B18.bmp" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;What I'd like to do in the future is to increase the baud rate to &amp;gt;100kbps and increase the cable length. Should be interesting to see how bad the ringing will get. And how it will affect data integrity. Something like the following is expected.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-MfOdEAcTJbQ/TlyEfTDugwI/AAAAAAAAAwk/BmxrunuIPNc/s1600/ti%2Brs422%2Bscope%2Breading.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://2.bp.blogspot.com/-MfOdEAcTJbQ/TlyEfTDugwI/AAAAAAAAAwk/BmxrunuIPNc/s400/ti%2Brs422%2Bscope%2Breading.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;That's from a lab test performed by &lt;a href="http://www.ti.com/lit/an/slla070d/slla070d.pdf"&gt;Texas Instruments&lt;/a&gt;. Baud rate was 200kbps. 100 feet of Bertek 100-ohm AWG24 twisted pair cable was used. There was one driver and only one receiver (TI AM26C31C and AM26C32C).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-6459944620139278748?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/6459944620139278748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/taking-peek-at-rs-485-signals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6459944620139278748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6459944620139278748'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/taking-peek-at-rs-485-signals.html' title='Taking a peek at RS-485 signals'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-vwYGf3pQYK8/TlusilQ5Q7I/AAAAAAAAAuc/QL7pNlgw0Ug/s72-c/SN75176B%2B1.bmp' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-2653335542714564801</id><published>2011-08-28T18:00:00.003+08:00</published><updated>2011-08-28T18:15:44.185+08:00</updated><title type='text'>Using RS-485 transceivers</title><content type='html'>The beauty of using RS-485 (more formally known as the TIA/EIA-485  standard) for long distance serial communication is that because it uses  differential lines it's largely immune to common mode voltages and  noise. It's also utterly simple electrically--at least in a half duplex  setup--in that it uses only one twisted pair cable and a common ground. &lt;br /&gt;&lt;br /&gt;Here's a setup for perhaps the simplest possible configuration: there's  only one transmitter and one or more receivers. In other words  communications is a &lt;i&gt;simplex&lt;/i&gt; type--information goes only one way.  The transmitter sends out data while the receivers intercept the data  stream but do not transmit anything.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hRzFOkKQHt4/Tld4yAbyDEI/AAAAAAAAAuU/Zg3XWap8eOs/s1600/RS485-simplex-transmitter.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://4.bp.blogspot.com/-hRzFOkKQHt4/Tld4yAbyDEI/AAAAAAAAAuU/Zg3XWap8eOs/s400/RS485-simplex-transmitter.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;In  the above circuit the non-inverting input of the unity gain buffer is  connected to a sensor circuit with an analog output. The MCU's ADC  converts the sensor reading to a digital value and sends it out  serially. U1 is a RS-485 transceiver. Pin DE and /RE are controlled by  the MCU, switching them high only when transmitting. &lt;br /&gt;&lt;br /&gt;At the heart of the RS-485 is the transceiver. In fact RS-485 is merely  an electrical standard. And the particular electrical characteristics of  the transceivers determine whether one has a RS-485 circuit or not.  There are a lot of transceivers out there and I would rather use  something like Maxim's &lt;a href="http://www.maxim-ic.com/datasheet/index.mvp/id/1111"&gt;MAX48x&lt;/a&gt;  series, but the price is simply eye popping! They're as expensive as  instrumentation amplifiers. Instead I've been using the jurassic &lt;a href="http://focus.ti.com/docs/prod/folders/print/sn75176b.html"&gt;SN75176B&lt;/a&gt;  which is over five times cheaper. Texas Instruments says it's rated for  10Mbps throughput. But hey my requirements are 2 to 3 orders of  magnitude lower.&lt;br /&gt;&lt;br /&gt;The problem with the 757176 is that it's a gas guzzler. An informal test  shows that it draws some 16mA @Vcc = 5V when it's set up for reception  only (pins DE and /RE are low). This shoots up to around 32mA when it's  in transmission mode (pins DE and /RE are high). As &lt;a href="http://www.eevblog.com/"&gt;Dave Jones&lt;/a&gt; would say, you can fly to moon on 32mA!&lt;br /&gt;&lt;br /&gt;To limit the amount of juice it gulps, we can--at least in a simplex  arrangement--turn off the 75176 when no transmission is being performed.  As can be seen in the schematic above, a PNP transistor is used to  power it up/down. When transmission of a data packet is about to  commence the transceiver is switched on and right after the last stop  bit is sent, the 75176 is powered down. Average power consumption of the  transmitter circuit will depend on how frequent transmissions are made.  It will also depend on the baud rate--the higher, the less time every  transmission takes and therefore the shorter the time the transceiver  will be on.&lt;br /&gt;&lt;br /&gt;In half duplex systems where drivers (transmitters) are also receivers  powering down the transceiver will probably be not an option.&lt;br /&gt;&lt;br /&gt;A very important issue with these transceivers is to ensure that they will be &lt;i&gt;fail-safe&lt;/i&gt;.  Fail safe in this context means that even if the transmitter's DE pin  is grounded--ie., the driver is disabled and lines B and A become  tri-stated to high impedance--or if power to the transmitting  transceiver is cut off as it is in the circuit above--any and all the  receivers will still see a differential voltage between B and A that's  &amp;gt; 200mV. As per the RS-485 standard, if -200mV &amp;lt; B - A &amp;lt;  +200mV, then output of pin R will be indeterminate. To guard against  this &lt;i&gt;bias resistors&lt;/i&gt; are added to lines A and B. In the schematic above these are pull-up resistor Rb1 and pull-down resistor Rb2.&lt;br /&gt;&lt;br /&gt;Very important note: The RS-485 standard states that when the difference  between the voltages between B and A is &amp;gt; +200mV (i.e, B - A &amp;gt;  200mV) then a logic 1 is output on the receiver, and when B - A &amp;lt;.  -200mV then a logic 0 is output. But chip manufacturers like Texas  Instruments and Maxim have reversed this. And so B is A and A is B on  their transceivers. No problem as long you don't mix transceivers that  use the RS-485 standard labeling. From hereon I'll follow the TI / Maxim  convention.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The values of the bias resistors determine the differential voltage A -  B. How much bias resistance should be installed depends on how many  receivers are connected to the network. For the transceiver used above,  each receiver has a minimum input resistance of 12Kohms. The more  receivers there are the less the total resistance there is since the  input resistances are in parallel. This translates to lower value bias  resistors. Here's how to compute the values:&lt;br /&gt;&lt;br /&gt;Let:&lt;br /&gt;R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; = total input resistance &lt;br /&gt;I = current through R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; &lt;br /&gt;V&lt;sub&gt;CC&lt;/sub&gt; = power supply voltage to which pull-up resistor will be connected&lt;br /&gt;R&lt;sub&gt;b&lt;/sub&gt; = bias resistor value&lt;br /&gt;R&lt;sub&gt;T&lt;/sub&gt; = total resistance which includes the bias resistors and R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;We require that the voltage across the total input resistance be greater than 200mV:&lt;br /&gt;&lt;br /&gt;I R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; &amp;gt; 200mV &lt;br /&gt;&lt;br /&gt;Solving for current:&lt;br /&gt;&lt;br /&gt;I &amp;gt; 200mV / R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;The bias resistors and R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; are in series and form a voltage divider and we define their sum as the total resistance:&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;T&lt;/sub&gt; = 2R&lt;sub&gt;b&lt;/sub&gt; + R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;It's also equal to:&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;T&lt;/sub&gt; = V&lt;sub&gt;CC&lt;/sub&gt; / I &lt;br /&gt;&lt;br /&gt;Since we're looking for the value of the bias resistors:&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;b&lt;/sub&gt; = (R&lt;sub&gt;T&lt;/sub&gt; - R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt;)/2&lt;br /&gt;&lt;br /&gt;Substituting and simplifying we obtain:&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;b&lt;/sub&gt; &amp;lt; (5V&lt;sub&gt;CC&lt;/sub&gt; - 1)R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt; / 2&lt;br /&gt;&lt;br /&gt;Example: Let's say we have two SN75176B transceivers on the network configured as receivers. V&lt;sub&gt;CC &lt;/sub&gt;= 5V. Each transceiver has an input resistance of 12Kohms. Because they're in parallel, R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt;  = 6K. Using the formula above, we find that bias resistors should be  less than 72Kohms each in order for A - B to be &amp;gt; 200mV when  transmitter is disabled (lines A and B are in a high impedance state).&lt;br /&gt;&lt;br /&gt;If &lt;i&gt;termination&lt;/i&gt; resistors are present then total resistance would be = R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt;||  termination resistors. So if two 120 ohm termination resistors were  present (termination resistors are usually between 120 and 150 ohms when  using UTP cables) and the R&lt;sub&gt;i&lt;sub&gt;total&lt;/sub&gt;&lt;/sub&gt;= 6K then the  total resistance = 6K || 120 || 120 = 1/(1/6000 + 1/120 + 1/120) = 59.4  ohms. Computing for the bias resistors we obtain 720 ohms. Because the  very low resistance values of termination resistors, as much as possible  we don't want to install them since they significantly increase power  consumption. &lt;br /&gt;&lt;br /&gt;Termination resistors are used across lines A and B for impedance  matching to prevent/minimize signal reflections leading to ringing. So  when do we need termination? Well, it depends if we have a short or long  line/cable. One heuristic for determining whether a line is long or  short is as follows.&lt;br /&gt;&lt;br /&gt;Let:&lt;br /&gt;t&lt;sub&gt;t&lt;/sub&gt; = transition time also known as rise/fall time of transceiver's transmitter signal in seconds&lt;br /&gt;R&lt;sub&gt;P&lt;/sub&gt; = propagation rate of electrical signals in copper = 0.2m/ns = 2 x 10&lt;sup&gt;8&lt;/sup&gt;m/s&lt;br /&gt;L = cable length in meters&lt;br /&gt;&lt;br /&gt;A line is considered long if: &lt;br /&gt;&lt;br /&gt;t&lt;sub&gt;t&lt;/sub&gt; &amp;lt; 2(L/R&lt;sub&gt;P&lt;/sub&gt;)&lt;br /&gt;&lt;br /&gt;(see &lt;a href="http://www.maxim-ic.com/app-notes/index.mvp/id/1090"&gt;Maxim&lt;/a&gt;'s equation for a more conservative figure).&lt;br /&gt;&lt;br /&gt;According to the SN75176B datasheet its t&lt;sub&gt;t&lt;/sub&gt; = 20ns. Let's solve for L to see how long the cable can be before it's deemed long. &lt;br /&gt;&lt;br /&gt;L = t&lt;sub&gt;t&lt;/sub&gt;R&lt;sub&gt;P&lt;/sub&gt;/2 = (20 x 10&lt;sup&gt;-9&lt;/sup&gt;)(2 x 10&lt;sup&gt;8&lt;/sup&gt;)/2 = 2 meters.&lt;br /&gt;&lt;br /&gt;So if we're using the SN75176B and our cable is longer than 2 meters  termination resistors are necessary. That's not the end of the story,  however. We may still be able to get away without having any termination  even at cable lengths much longer than this:&lt;br /&gt;&lt;blockquote&gt;If the rise time is unknown, another way of deciding whether  a line is long or short is to compare the shortest expected bit width  and the 1-way cable delay. This method considers two factors: the  reflections may bounce back and forth several times before settling, and  the bit rates at the transmitter and receiver may vary slightly from  each other. As a general guideline, if the bit width is 40 or more times  greater than the delay, any reflections will have settled by the time  the receiver reads the bits. (Axelson, p.111)&lt;/blockquote&gt;&lt;br /&gt;The key to obviating the need for termination is to have a sufficiently  slow baud rate so that reflections in the transmission line (a "long  line") have already died out by the time the microcontroller reads the  bit. The 1-way cable delay is merely L/R&lt;sub&gt;P&lt;/sub&gt;. Thus for a  100-meter wire, the 1-way cable delay = 500ns or 0.5µs. Using the above  guideline we can determine the maximum cable length given a particular  transmission baud rate--the bit width is the reciprocal of baud rate. &lt;br /&gt;&lt;br /&gt;Let:&lt;br /&gt;B = transmission baud rate in bits/sec&lt;br /&gt;R&lt;sub&gt;P&lt;/sub&gt; = propagation rate of electrical signals in copper = 0.2m/ns = 2 x 10&lt;sup&gt;8&lt;/sup&gt;m/s&lt;br /&gt;L = cable length in meters &lt;br /&gt;&lt;br /&gt;The guideline states that:&lt;br /&gt;&lt;br /&gt;1/B &amp;gt;= 40(L/R&lt;sub&gt;P&lt;/sub&gt;)&lt;br /&gt;&lt;br /&gt;Solving for L we have:&lt;br /&gt;&lt;br /&gt;L &amp;lt;= [(1/B)/40]R&lt;sub&gt;P&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;L &amp;lt;= R&lt;sub&gt;P&lt;/sub&gt;/(40B)&lt;br /&gt;&lt;br /&gt;For a baud rate of 19,200, L = 260 meters. So theoretically no  termination is necessary if cable length is less than this. Just out of  curiosity let's see the maximum baud rate at which we can transmit for the  2-meter cable above.&lt;br /&gt;&lt;br /&gt;B = R&lt;sub&gt;P&lt;/sub&gt;/(40L) = 2.5Mbps&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;References:&lt;br /&gt;&lt;br /&gt;Janet Axelson, &lt;i&gt;Serial Port Complete: COM Ports, USB Virtual COM Ports, and Ports for Embedded Systems&lt;/i&gt;, 2ed, Lakeview Research LLC, 2007, Chapters.6 &amp;amp; 7.&lt;br /&gt;&lt;a href="http://www.bb-europe.com/tech_articles/polarities_for_differential_pair_signals.asp"&gt;&lt;br /&gt;Polarities for Differential Pair Signals (RS-422 and RS-485)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.bb-elec.com/bb-elec/literature/tech/485appnote.pdf"&gt;RS-422 and RS-485 Application Note&lt;/a&gt; (computation of bias resistor values)&lt;br /&gt;&lt;a href="http://pdfserv.maxim-ic.com/en/an/AN1090.pdf"&gt;&lt;br /&gt;AN1090 Methods for Trimming the Power Required in RS-485 Systems&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-2653335542714564801?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/2653335542714564801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/using-rs-485-transceivers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2653335542714564801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/2653335542714564801'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/using-rs-485-transceivers.html' title='Using RS-485 transceivers'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-hRzFOkKQHt4/Tld4yAbyDEI/AAAAAAAAAuU/Zg3XWap8eOs/s72-c/RS485-simplex-transmitter.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-516534831109792621</id><published>2011-08-17T00:03:00.001+08:00</published><updated>2011-08-17T00:20:41.975+08:00</updated><title type='text'>ADC LSb mystery</title><content type='html'>Does a 10-bit ADC really have a &lt;i&gt;resolution&lt;/i&gt; (not accuracy) of 10 bits? Apparently it might not. I've been having problems with the LSb (least significant bit) of the ADC output of PICs. What's happening is that even with a stable, fixed-voltage input signal to the ADC, the digitized output--depending on the V&lt;sub&gt;DD&lt;/sub&gt; level and the input voltage signal--may either be stable or the value flip flops between two values (one LSb apart) from one conversion to the next.&lt;br /&gt;&lt;br /&gt;The LSb problem is dependent on both V&lt;sub&gt;DD&lt;/sub&gt; and ADC input signal level. For  example the problem might not occur at input signal X volts with a V&lt;sub&gt;DD&lt;/sub&gt; = 4.950V but will appear when V&lt;sub&gt;DD&lt;/sub&gt; is within the range of 3.0 to 3.6V. And/or it might get worse (i.e., the number of times the LSb changes becomes more frequent) as one approaches a certain voltage range. Or for a certain signal voltage Y, the problem may be nonexistent at all  with V&lt;sub&gt;DD&lt;/sub&gt; from 2.5 to 5.0V. Or for signal Z the the LSb glitch may be present at practically the entire  operating voltage range of the MCU/ADC. &lt;br /&gt;&lt;br /&gt;I've tried everything I can think of to get rid of this hair-tearing problem: I've used a linear power supply instead of a switchmode to minimize power supply noise. I've used separate power and ground lines for digital and analog and employed star connections. Actually digital noise should be minimal since, as we'll see, the MCU goes to sleep when the ADC is reading. I've also peppered the circuit with filter and bypass caps. The circuit is in open air but I still allowed it to settle down thermally so drifts due to temperature are eliminated. I admit the circuit is breadboarded and not soldered onto a PCB and so contact problems may be the culprit. To minimize that possibility I've used redundant connections. I also tried buffering the voltage signal using an op amp. Though there has been a reduction, the above solutions have not completely eliminated the problem.&lt;br /&gt;&lt;br /&gt;Here's the test circuit: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Mkxxk5b7o24/TkqHud8KNfI/AAAAAAAAAs0/rEmMqEUc2YM/s1600/adc-lsb-schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="158" src="http://4.bp.blogspot.com/-Mkxxk5b7o24/TkqHud8KNfI/AAAAAAAAAs0/rEmMqEUc2YM/s400/adc-lsb-schematic.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Bridge rectifier output is approximately 12VAC. Potentiometer is a 5-ohm multi-turn Bourns 3590S-2-502L. Because this is the only highly reliable pot that I have and because output voltage of the LM317 must not exceed 5V (the PIC's max VDD ), I had to add the 560 and 220 ohm resistors. When the pot is turned all the way to zero, the 560 ohm resistor is shunted leaving only the 220 ohms. When the pot is at its maximum 5K, it's in parallel with the 560 giving a net resistance of 504 ohms. This in turn is in series with the 220 ohm, giving a total of 724 ohms. Plug those values into the LM317 equation and the effective output range of this power supply is 2.4 to 5.0V. &lt;br /&gt;&lt;br /&gt;Firmware is as follows. It's the 3rd or 4th revision. The ADC uses V&lt;sub&gt;DD&lt;/sub&gt; as its positive reference and  ground as  its negative voltage reference. Thus the ADC output will be  ratiometric  with respect to the power supply. This way the analog to  digital  conversion is immune to drifts in the power supply. ADC read is  performed approximately every 8ms. Because the PIC12F1822's  ADC has silicon flaws, ADC read is performed during sleep, thus  although timer0 tick is set to 8ms, several hundred microseconds are  added while the system clock is shut down. The ADC read takes so long because there are actually two sets of reads. One is a single read and the other is a set of 16 reads which are then averaged. I tried averaging the reads to see if I could get obtain a value that doesn't keep flipping around. After the reads, the current and previous reads are compared. If they're different then the corresponding MCU pins (&lt;code&gt;neflag&lt;/code&gt; and &lt;code&gt;neflagave&lt;/code&gt;) are made high, else they're made low. Those pins are connected to an oscilloscope and logic analyzer so I can see how (un)stable the ADC reads are. In this final revision of the firmware I've also enabled the EUSART so I can see by how many bits the ADC reads differ (the &lt;a href="http://www.saleae.com/logic/"&gt;Saleae Logic&lt;/a&gt; analyzer automatically decodes the serial data stream). Only the lower byte of the ADC output (register &lt;code&gt;ADRESL&lt;/code&gt;) is transmitted. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/*&lt;br /&gt;&lt;br /&gt;processor = PIC 12F1822&lt;br /&gt;compiler = mikroC Pro v.5.0.0&lt;br /&gt;August 2011&lt;br /&gt;&lt;br /&gt;Test of ADC LSb&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;internal oscillator is used, WDT disabled, power up timer and brown out reset disabled, MCLR disabled&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define  neflag              PORTA.f5            // not equal flag; 1 = previous and current ADC values are not equal&lt;br /&gt;#define  neflagave           PORTA.f4            // not equal flag average; 1 = previous and current averaged ADC values are not equal&lt;br /&gt;#define  t0ini               256 - 250           // value loaded into TMR0 every time it overflows&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void IniReg()&lt;br /&gt;{&lt;br /&gt;  OSCCON    = 0b1101000;     // internal clock 4Mhz&lt;br /&gt;  TRISA     = 0b100;&lt;br /&gt;  ANSELA    = 0b100;&lt;br /&gt;  PORTA     = 0;&lt;br /&gt;&lt;br /&gt;  TMR0      = t0ini;&lt;br /&gt;  OPTION_REG = 0b10000100;   // Weak pull ups disabled&lt;br /&gt;                             // timer0 uses internal clock,&lt;br /&gt;                             // prescaler assigned to timer0, prescaler = 1:32&lt;br /&gt;&lt;br /&gt;  ADCON0 = 0b1001;           // channel 2, ADC on&lt;br /&gt;  ADCON1 = 0b11110000;       // right justified, use Frc, Vdd as positive reference voltage&lt;br /&gt;  INTCON.GIE = 0;            // global interrupt disabled&lt;br /&gt;  PIE1.ADIE = 1;             // ADC interrupt enabled&lt;br /&gt;&lt;br /&gt;  // baud rate = 2400&lt;br /&gt;  SPBRGH = 0;&lt;br /&gt;  SPBRGL = 25;&lt;br /&gt;  RCSTA.SPEN = 1;&lt;br /&gt;  TXSTA.TXEN = 1;&lt;br /&gt;  TXSTA.SYNC = 0;&lt;br /&gt;&lt;br /&gt;} // void IniReg()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* =========================================================================================================&lt;br /&gt;&lt;br /&gt;According to PIC12F1822 Silicon Errata sheet DS80502C the ADC unit in certain silicon revisions is buggy:&lt;br /&gt;&lt;br /&gt;"Under certain device operating conditions, the ADC conversion may not complete properly.&lt;br /&gt;When this occurs, the ADC Interrupt Flag (ADIF) does not get set,&lt;br /&gt;the GO/DONE bit does not get cleared and the conversion result&lt;br /&gt;does not get loaded into the ADRESH and ADRESL result registers."&lt;br /&gt;&lt;br /&gt;mikroC Pro v5.00 compiler's Adc_Read() built-in ADC function uses the GO/DONE bit to check conversion completion (without putting the MCU to sleep, of course)&lt;br /&gt;so the function cannot be used. And indeed the MCU hangs (when WDT is disabled) or the MCU resets when WDT is enabled.&lt;br /&gt;&lt;br /&gt;The workaround used here is as per method 1 in the said errata:&lt;br /&gt;&lt;br /&gt;"Select the dedicated RC oscillator as the ADC conversion clock source,&lt;br /&gt;and perform all conversions with the device in Sleep."&lt;br /&gt;&lt;br /&gt;=========================================================================================================  */&lt;br /&gt;&lt;br /&gt;unsigned int ADC()&lt;br /&gt;{&lt;br /&gt;  PIR1.ADIF = 0;                       // clear ADC interrupt flag&lt;br /&gt;  INTCON.PEIE = 1;                     // peripheral interrupts enabled&lt;br /&gt;  ADCON0.GO = 1;                       // start ADC conversion&lt;br /&gt;  asm {sleep}                          // zzzzzzzzzzzzzzz.... when ADIF = 1 MCU will wake up and will continue execution of the program&lt;br /&gt;  PIR1.ADIF = 0;                       // clear ADC interrupt flag&lt;br /&gt;  INTCON.PEIE = 0;                     // disable peripheral interrupts&lt;br /&gt;  return ADRESH*256 + ADRESL;          // return the 10-bit ADC value as a 16-bit number&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void Compare()&lt;br /&gt;{&lt;br /&gt;  unsigned char i;&lt;br /&gt;  static unsigned int CURR = 0;        // current single ADC reading&lt;br /&gt;  static unsigned int CURRAVE = 0;     // current average of 16 ADC readings&lt;br /&gt;  static unsigned int PREV;            // previous single ADC reading&lt;br /&gt;  static unsigned int PREVAVE;         // previous average of 16 ADC reading&lt;br /&gt;  unsigned int SUM;                    // sum of 16 ADC readings&lt;br /&gt;&lt;br /&gt;  PREVAVE = CURRAVE;&lt;br /&gt;  SUM = 0;&lt;br /&gt;  for (i=0; i&amp;lt;16; i++)&lt;br /&gt;    SUM += ADC();&lt;br /&gt;&lt;br /&gt;  PREV = CURR;&lt;br /&gt;  CURR = ADC();&lt;br /&gt;  &lt;br /&gt;  TXREG = CURR;                        // bec ADC is performed during sleep while serial data transmission is dependent of the system clock&lt;br /&gt;                                       // transmission can only begin after all ADC reads are done&lt;br /&gt;&lt;br /&gt;  if (CURR == PREV)&lt;br /&gt;    neflag = 0;&lt;br /&gt;  else&lt;br /&gt;    neflag = 1;&lt;br /&gt;&lt;br /&gt;  CURRAVE = SUM / 16;&lt;br /&gt;  if (SUM % 16 &amp;gt;= 8)                   // number rounding algorithm&lt;br /&gt;    CURRAVE += 1;&lt;br /&gt;&lt;br /&gt;  if (CURRAVE == PREVAVE)&lt;br /&gt;    neflagave = 0;&lt;br /&gt;  else&lt;br /&gt;    neflagave = 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  IniReg();&lt;br /&gt;&lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (INTCON.TMR0IF)&lt;br /&gt;    {&lt;br /&gt;      TMR0 = t0ini;&lt;br /&gt;      Compare();&lt;br /&gt;      INTCON.TMR0IF = 0;&lt;br /&gt;    }&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I used the oscilloscope with time base on roll mode with one channel monitoring &lt;code&gt;neflag&lt;/code&gt; and other &lt;code&gt;neflagave&lt;/code&gt;. I opted not to take screenshots of the scope--would take too much effort. But here are some of the logic analyzer data at particular voltages. As you can see the averaging routine sometimes works, sometimes doesn't, depending on how bad the LSb problem is. Resistor values are as per their color code. V&lt;sub&gt;DD&lt;/sub&gt; values are actual measured voltages using a Fluke 87V.  &lt;br /&gt;&lt;br /&gt;I. Ra = 5.1K, Rb = 4.3K&lt;br /&gt;&lt;br /&gt;A. V&lt;sub&gt;DD&lt;/sub&gt; = 2.502V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-qmuSooUDm-o/TkqIKQ2dwbI/AAAAAAAAAs8/DxRd6jA5WZ8/s1600/adc%2Blsb%2B2.502V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-qmuSooUDm-o/TkqIKQ2dwbI/AAAAAAAAAs8/DxRd6jA5WZ8/s400/adc%2Blsb%2B2.502V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7nm4aAnx_PQ/TkqIKTHzl4I/AAAAAAAAAtE/f76JHbDz_JY/s1600/adc%2Blsb%2B2.502V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-7nm4aAnx_PQ/TkqIKTHzl4I/AAAAAAAAAtE/f76JHbDz_JY/s400/adc%2Blsb%2B2.502V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;B. V&lt;sub&gt;DD&lt;/sub&gt; = 4.000V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-RYKZrktat-A/TkqIj4dUgDI/AAAAAAAAAtU/weOmIcJ7bJA/s1600/adc%2Blsb%2B4.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-RYKZrktat-A/TkqIj4dUgDI/AAAAAAAAAtU/weOmIcJ7bJA/s400/adc%2Blsb%2B4.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-53F98m9yQd8/TkqIji5pB5I/AAAAAAAAAtM/YdmXwCLdCWs/s1600/adc%2Blsb%2B4.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-53F98m9yQd8/TkqIji5pB5I/AAAAAAAAAtM/YdmXwCLdCWs/s400/adc%2Blsb%2B4.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;C. V&lt;sub&gt;DD&lt;/sub&gt; = 5.000V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-QkB3b4qcFlo/TkqIt1WowOI/AAAAAAAAAtk/mlAh3VVpQGA/s1600/adc%2Blsb%2B5.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-QkB3b4qcFlo/TkqIt1WowOI/AAAAAAAAAtk/mlAh3VVpQGA/s400/adc%2Blsb%2B5.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-dSegCS87eZ4/TkqIt23dOaI/AAAAAAAAAtc/HDrDV7Yln1w/s1600/adc%2Blsb%2B5.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-dSegCS87eZ4/TkqIt23dOaI/AAAAAAAAAtc/HDrDV7Yln1w/s400/adc%2Blsb%2B5.000V%2B5.1K-4.3K%2B5M%2B250khz%2B2011aug14%2Bzoom.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;II. Ra = 5.1K || 330 = 310, Rb = 4.3K &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-3-nMadiTx7o/TkqI_XlGq3I/AAAAAAAAAts/lyoa5SbOqeE/s1600/adc%2Blsb%2B5.000V%2B310-4.3K%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-3-nMadiTx7o/TkqI_XlGq3I/AAAAAAAAAts/lyoa5SbOqeE/s400/adc%2Blsb%2B5.000V%2B310-4.3K%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I provide only a screenshot for V&lt;sub&gt;DD&lt;/sub&gt; = 5.000V. There is absolutely no LSb problem for V&lt;sub&gt;DD&lt;/sub&gt; = 2.5V to 5.0V&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;III. Ra = 5.1K, Rb = 4.3K || 330 = 306&lt;br /&gt;&lt;br /&gt;A. V&lt;sub&gt;DD&lt;/sub&gt; = 2.999V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ujGI4hkDQVo/TkqJlyrfJ7I/AAAAAAAAAt0/pkNlOPetwrs/s1600/adc%2Blsb%2B2.999V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-ujGI4hkDQVo/TkqJlyrfJ7I/AAAAAAAAAt0/pkNlOPetwrs/s400/adc%2Blsb%2B2.999V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;B. V&lt;sub&gt;DD&lt;/sub&gt; = 3.499V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Bho1SUtnmGU/TkqJmKJdwAI/AAAAAAAAAt8/4nP3v20nHG4/s1600/adc%2Blsb%2B3.499V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://2.bp.blogspot.com/-Bho1SUtnmGU/TkqJmKJdwAI/AAAAAAAAAt8/4nP3v20nHG4/s400/adc%2Blsb%2B3.499V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;C. V&lt;sub&gt;DD&lt;/sub&gt; = 4.500V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-bHrkXxO2ExA/TkqJ5JFyZtI/AAAAAAAAAuE/16oBkBIjIPw/s1600/adc%2Blsb%2B4.500V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-bHrkXxO2ExA/TkqJ5JFyZtI/AAAAAAAAAuE/16oBkBIjIPw/s400/adc%2Blsb%2B4.500V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;D. V&lt;sub&gt;DD&lt;/sub&gt; = 5.000V&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-HE8S4u7jCcQ/TkqJ5b9g8GI/AAAAAAAAAuM/L5Z-JD7jUPI/s1600/adc%2Blsb%2B5.000V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://2.bp.blogspot.com/-HE8S4u7jCcQ/TkqJ5b9g8GI/AAAAAAAAAuM/L5Z-JD7jUPI/s400/adc%2Blsb%2B5.000V%2B5.1K-306%2B5M%2B250khz%2B2011aug14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So what could be causing this ADC read glitch? Well, no ADC is 100% accurate. It will have various sources of errors--among them &lt;a href="http://www.maxim-ic.com/app-notes/index.mvp/id/641"&gt;INL, DNL, offset, and gain errors&lt;/a&gt;--due to silicon and its fabrication. But accuracy isn't our problem; it's resolution. Ostensibly none of these is the error that I've encountered. What it does look very much like is conversion error due to &lt;a href="http://www.winsystems.com/software/AN-23Noise.pdf"&gt;transition noise&lt;/a&gt;, also known as &lt;a href="http://www.analog.com/library/analogdialogue/archives/40-02/adc_noise.pdf"&gt;code-transition noise&lt;/a&gt; or &lt;a href="http://www.maxim-ic.com/app-notes/index.mvp/id/748"&gt;code-edge noise&lt;/a&gt;. &lt;a href="http://www.maxim-ic.com/app-notes/index.mvp/id/641"&gt;Transition noise&lt;/a&gt; is,&lt;br /&gt;&lt;blockquote&gt;the range of input voltages that cause an ADC output to toggle between adjacent output codes. As you increase the analog input voltage, the voltages that define where each code transition occurs (code edges) are uncertain due to the associated transition noise.&lt;/blockquote&gt;&lt;br /&gt;Keeping the input signal constant and performing lots of ADC reads, the distribution of ADC output codes is supposedly Gaussian (normal distribution). In the tests with the PIC, however, apparently there are only two codes, i.e, output varies by only one LSb. I haven't done it, but it should be easy to have the MCU read a thousand or ten thousand times and tell us whether the codes vary by more than one LSb. &lt;br /&gt;&lt;br /&gt;Interestingly, averaging, which I used in the firmware above, is the &lt;a href="http://www.analog.com/library/analogdialogue/archives/40-02/adc_noise.pdf"&gt;recommended algorithm&lt;/a&gt; to address LSb flickering. However, as we've seen even averaging (at least the way it was implemented above) isn't a panacea. Maybe I'm doing it wrong. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-516534831109792621?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/516534831109792621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/adc-lsb-mystery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/516534831109792621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/516534831109792621'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/08/adc-lsb-mystery.html' title='ADC LSb mystery'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-Mkxxk5b7o24/TkqHud8KNfI/AAAAAAAAAs0/rEmMqEUc2YM/s72-c/adc-lsb-schematic.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7344560874392216916</id><published>2011-07-27T01:24:00.000+08:00</published><updated>2011-07-27T01:24:47.604+08:00</updated><title type='text'>Is it dawn yet? How shall we know?</title><content type='html'>Those of you who've been following the development of the &lt;a href="http://electromotiveforces.blogspot.com/2011/07/digital-filter-for-auto-night-light.html"&gt;MCU-based ANLC&lt;/a&gt; will probably remember that I turned to a micrcocontroller since I want to switch the load off not at the ambient light level (corresponding sensor voltage) at which we want to turn on the load, nor at the load turn-on ambient light level + some hysteresis, but rather at the very crack of dawn, which corresponds to a light level significantly less than that at load turn-on. So you might be wondering what algorithm I'm currently using to implement that.&lt;br /&gt;&lt;br /&gt;With all other factors (clouds, city lights, etc.) kept constant, one characteristic of dawn is that the light level rises evenly and continuously (thanks to the very stable rotation speed of the earth and nearly constant light output of the sun). And this increase will not exceed a certain dV/dt--which we've measured (at least in my particular location in the globe which happens to be 15° north of the equator) to be almost &lt;a href="http://electromotiveforces.blogspot.com/2011/07/more-decent-measurement-of-dawn-and.html"&gt;6mV/s&lt;/a&gt; (@Vdd = 5V). &lt;br /&gt;&lt;br /&gt;So one way of ruling out other light sources is to look at whether dV/dt &amp;gt; 0 but &amp;lt; 6mV/s and whether this is sustained over time. If it's currently very dark (night time) and these conditions are met then there is a good probability we are witnessing dawn/sunrise. If not then there is a good chance the light source isn't the sun. &lt;br /&gt;&lt;br /&gt;In addition we'd want to monitor for the above conditions as close to actual dawn as possible. We don't, for instance, want to check after just a couple of hours after dusk. And speaking of time of day, our circuit doesn't actually know what time of day it really is. For instance just because ambient light level has dropped below the trip point doesn't mean it really is dusk. It may be midday but the sky is so heavy with dark nimbus clouds that the light level has dipped to a point that the load has been turned on. The algorithm needs to take this into consideration. &lt;br /&gt;&lt;br /&gt;One way of gaining confidence that it really is nighttime is to monitor the amount of time the load has been on. If the shortest night during the entire year is, say 10 hours, then we can be fairly sure that it's night if the load has been on for at least 8 hours. We can thus wait 8 hours after the load has been on before beginning to check for dawn conditions. &lt;br /&gt;&lt;br /&gt;We can also monitor the sensor voltage to find out if it's really nighttime, but the night time reading/value will depend on where we install the sensor and how much artificial light sources will be polluting it. As I mentioned before I placed the sensor on the roof and shielded it on the sides to minimize the effects of streetlights, billboard lights, et al. &lt;br /&gt;&lt;br /&gt;Besides just waiting x hours to begin monitoring for dawn dV/dt, we also want to find out the maximum light level for an hour or two before the time we begin checking for dawn because we only want to start applying our dawn dV/dt criteria when ambient light level exceeds this value. Given that this is the maximum ambient light level close to dawn, there is a greater probability that light whose intensity exceeds it and which keeps increasing but has a rate of increase less than the max dV/dt is in fact indicative of the first rays of the sun trickling in and not some other light source. &lt;br /&gt;&lt;br /&gt;While I've implemented the above set of detection conditions for several days now--and so far so good--I imagine they do not provide a foolproof way of determining the onset of dawn. For example, if our firmware is already monitoring for dawn conditions and clouds begin rolling in, gradually reflecting more of the city lights thus increasing the sky light level and if the clouds move in at just the right rate so that max dV/dt isn't exceeded, then our firmware will be fooled into switching the load off.&lt;br /&gt;&lt;br /&gt;One way of further improving the system is to add another sensor. We set the sensors up such that one receives light mainly from the eastern part of sky while the other from the western half (this can be as simple as having the LDRs side by side with an opaque board between them to block light from the eastern/western side of the sky from falling on the west/east LDR). We can then monitor for differences in light levels detected. Surely the eastern part of the sky will be brighter at dawn. As a bonus we can use this setup to detect dusk as well.&lt;br /&gt;&lt;br /&gt;For now, however, I'm sticking to a simple single-LDR sensor. There won't be need to make it any more complex if the algorithm works or a better algorithm can improve dawn detection.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-7344560874392216916?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/7344560874392216916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/is-it-dawn-yet-how-shall-we-know.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7344560874392216916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7344560874392216916'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/is-it-dawn-yet-how-shall-we-know.html' title='Is it dawn yet? How shall we know?'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-1495185117366035667</id><published>2011-07-25T00:28:00.000+08:00</published><updated>2011-07-25T00:28:27.557+08:00</updated><title type='text'>A more decent measurement of dawn and dusk dV/dt</title><content type='html'>I previously estimated the maximum dV/dt of the &lt;a href="http://electromotiveforces.blogspot.com/2011/07/digital-filter-for-auto-night-light.html"&gt;MCU-based ANLC sensor&lt;/a&gt; during dusk and dawn at 4.5mV/s, but I want to have a more accurate figure. Because I don't have the proper test gear, I had to make do with a DMM and a stopwatch.&lt;br /&gt;&lt;br /&gt;I decided to keep dV constant at 0.1V and record the time it took for the sensor voltage to increase/decrease by that amount. Thus what I was recording was in fact dt/dV. It would then be a simple matter of getting the reciprocal to obtain dV/dt.&lt;br /&gt;&lt;br /&gt;The stopwatch of my Nokia cell phone has a "split timing" feature that allows the user to press the button and have a reading without actually stopping the stopwatch. Unfortunately, while using it I found out that it has a limit of 20 split timing readings. So as you will see, there are breaks in the data and graph. Those are the times when I had to reset the stopwatch. Too bad I just discovered this &lt;a href="http://www.online-stopwatch.com/split-timer/"&gt;online split timer and countdown timer&lt;/a&gt;. Would've been great to use it. &lt;br /&gt;&lt;br /&gt;The July 23 readings were a spur of the moment thing. I didn't actually plan to record the values. And so data on that date are quite limited.&lt;br /&gt;&lt;br /&gt;Enough intro. Here's the &lt;a href="https://spreadsheets.google.com/spreadsheet/ccc?key=0AuWFzMAGPO9vdFFoTWtMMlJJdkFEbDZHTlRtZE4yTVE&amp;amp;hl=en_US"&gt;spreadsheet&lt;/a&gt; with all the data, computations and graphs. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-9bdt-Bf0_Lc/TixGlmV8NBI/AAAAAAAAAsk/qvXUoGvrXh0/s1600/dV%2Bdt%2Bdawn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="159" src="http://1.bp.blogspot.com/-9bdt-Bf0_Lc/TixGlmV8NBI/AAAAAAAAAsk/qvXUoGvrXh0/s320/dV%2Bdt%2Bdawn.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-1HectGBCEbc/TixGlkESiKI/AAAAAAAAAss/HPTZe3saD3c/s1600/dV%2Bdt%2Bdusk.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="165" src="http://3.bp.blogspot.com/-1HectGBCEbc/TixGlkESiKI/AAAAAAAAAss/HPTZe3saD3c/s320/dV%2Bdt%2Bdusk.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It's clear from the table and graphs that the maximum dV/dt is almost 6mV/s. Two things of interest: 1. The curves for dawn and dusk apparently don't have the same shape. Wonder if more data will even out the differences or if this difference is intrinsic. 2. The July 24 dusk graph is shifted downwards compared to the previous day's. This is probably because it was rather overcast on the 24th. That dV/dt can be attenuated by cloud cover needs to be noted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-1495185117366035667?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/1495185117366035667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/more-decent-measurement-of-dawn-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1495185117366035667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/1495185117366035667'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/more-decent-measurement-of-dawn-and.html' title='A more decent measurement of dawn and dusk dV/dt'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-9bdt-Bf0_Lc/TixGlmV8NBI/AAAAAAAAAsk/qvXUoGvrXh0/s72-c/dV%2Bdt%2Bdawn.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7948169580260806331</id><published>2011-07-22T13:19:00.001+08:00</published><updated>2011-07-22T13:23:02.275+08:00</updated><title type='text'>Digital filter for auto night light</title><content type='html'>In any automatic night light circuit that's meant to switch on perimeter security lights, the time period we are most interested in is dusk and dawn. We want the lights to be switched on at dusk and turned off at dawn. (Whether the lights are turned on/off earlier or later is dependent on the user.) During the night we want the lights on regardless of lightning flashes or car headlamps shining on the sensor or bright city lights being reflected by the clouds or a full moon or what have you. During the daytime we want the lights off regardless of clouds rolling in and out and thus momentarily blocking the sun, or a very overcast day, or a heavy downpour. Of course we would want the lights turned on even during the middle of the day if for some reason utter darkness descended such as during a (near) total solar eclipse, or--if we wanted to test the circuit--if we covered the sensor with some opaque material.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-848l3JC5CJc/TihDTxnysiI/AAAAAAAAArg/73dlpmFrovQ/s1600/anls%2Bsensor%2Banalog.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="121" src="http://4.bp.blogspot.com/-848l3JC5CJc/TihDTxnysiI/AAAAAAAAArg/73dlpmFrovQ/s320/anls%2Bsensor%2Banalog.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;I've discussed the following light sensor &lt;a href="http://electromotiveforces.blogspot.com/2011/05/automatic-night-light-circuits.html"&gt;here&lt;/a&gt; and &lt;a href="http://electromotiveforces.blogspot.com/2011/06/all-analog-automatic-night-light.html"&gt;here&lt;/a&gt;. Just a summary of how it works: R1 and LDR form a voltage divider. When the voltage across LDR is greater than across the 10uF capacitor, the cap gets charged via D1. When voltage across the cap is greater than across LDR, the capacitor discharges via R2. Given how D1 shunts R2 during charging and given that R1 has a much lower value than R2, the net result is that charging rate is faster than discharge rate. This means that when there is an abrupt and large increase in the level of ambient light (LDR resistance decreases) the voltage across the cap decreases slower compared to when there is sudden and large decrease in light level (LDR resistance increases). In other words, the circuit has a greater inertia to positive changes in light level than to negative changes in light level. During daytime, assuming the sensor were located outdoors and were we to shroud it with a black opaque bag, the voltage output would within seconds rise close to VDD. Were we then to take off the bag, the voltage would drop back to near ground level but a slower rate than when darkness fell upon the LDR. &lt;br /&gt;&lt;br /&gt;Recently I provided an&amp;nbsp; &lt;a href="http://electromotiveforces.blogspot.com/2011/07/microcontroller-based-automatic-night.html"&gt;update on the MCU-based ANLC I'm testing&lt;/a&gt;. It's still a work in progress but I thought I'd provide more information on the algorithm I'm using to try and mimic the asymmetric filtering action of the analog circuit above.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-hkMWn8LWVps/TijhO8TfEkI/AAAAAAAAAsY/XmMXS96o60o/s1600/anls%2Bsensor%2Bdigital.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="159" src="http://1.bp.blogspot.com/-hkMWn8LWVps/TijhO8TfEkI/AAAAAAAAAsY/XmMXS96o60o/s320/anls%2Bsensor%2Bdigital.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;In the MCU version of the ANLC the LDR and fixed resistors have switched places. Sure we can retain the fixed resistor as the pull-up, but I find it more intuitive associating a low voltage sensor output with darkness and near VDD output as bright light condition. &lt;br /&gt;&lt;br /&gt;One way of implementing a low pass filter digitally is to compute for  the moving average (MA). We can read the ambient light level at fixed  time intervals and store the values in a one-dimensional array.  Every time a new reading is performed the oldest reading is discarded and  the new reading stored. The readings are summed and average computed. &lt;br /&gt;&lt;br /&gt;One  problem with the MA filter is that it will not provide the asymmetric  filtering. As the graphs below show, an MA filter  reacts the same way whether the light level is increasing or decreasing.  Another characteristic of the MA filter is that a large number of  readings are required to sufficiently attenuate light level changes that have a high  dV/dt (change in voltage over change in time). Having a large number of  readings is analogous to having large capacitance. A large cap takes  longer to charge/discharge. More readings means there is more inertia to  new readings. But lots of stored readings translates to memory guzzling, specially  because 16-bit values are going to be stored. Because it can't meet the specs we have to conclude that  the MA filter is unsuitable for this application.&lt;br /&gt;&lt;br /&gt;Because the charge and discharge rates of the capacitor in the analog circuit above is what provides the filtering action, we can implement this digitally by limiting the dV/dt of light level readings. Here dt translates to the interval between reads by the analog-to-digital converter. If this interval is fixed then we need only deal with dV. Because we want to have more inertia against increasing light levels we can limit the dV when it is positive much more strongly when dV is negative. This is easily done in firmware by comparing the previous and current light level readings. If the difference exceeds the maximum positive (or negative dV), then we only store a value equal to the previous reading plus the positive dV (or minus the negative dV).&lt;br /&gt;&lt;br /&gt;Letting&lt;br /&gt;&lt;br /&gt;dV&lt;sup&gt;+&lt;/sup&gt; = maximum permitted change in voltage if voltage is increasing&lt;br /&gt;dV&lt;sup&gt;-&lt;/sup&gt; = maximum permitted change in voltage if voltage is decreasing&lt;br /&gt;V&lt;sub&gt;i&lt;/sub&gt; = current voltage reading -- will be adjusted to meet max dV&lt;br /&gt;V&lt;sub&gt;i-1&lt;/sub&gt; = previous voltage reading -- already adjusted to meet max dV &lt;br /&gt;&lt;br /&gt;and assuming that V&lt;sub&gt;i&lt;/sub&gt; contains the latest raw/unmanipulated ADC reading, the algorithm for what we can call an &lt;i&gt;asymmetric dV attentuation&lt;/i&gt; or ADVA filter is as follows (written in pseudo C):&lt;br /&gt;&lt;br /&gt;if (V&lt;sub&gt;i&lt;/sub&gt; &amp;gt; V&lt;sub&gt;i-1&lt;/sub&gt; &amp;amp;&amp;amp; V&lt;sub&gt;i&lt;/sub&gt; - V&lt;sub&gt;i-1&lt;/sub&gt; &amp;gt; dV&lt;sup&gt;+&lt;/sup&gt;)&lt;br /&gt;&amp;nbsp; V&lt;sub&gt;i&lt;/sub&gt; = V&lt;sub&gt;i-1&lt;/sub&gt; + dV&lt;sup&gt;+&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;if (V&lt;sub&gt;i&lt;/sub&gt; &amp;lt; V&lt;sub&gt;i-1&lt;/sub&gt; &amp;amp;&amp;amp; V&lt;sub&gt;i-1&lt;/sub&gt; - V&lt;sub&gt;i&lt;/sub&gt; &amp;gt; dV&lt;sup&gt;-&lt;/sup&gt;)&lt;br /&gt;&amp;nbsp; V&lt;sub&gt;i&lt;/sub&gt; = V&lt;sub&gt;i-1&lt;/sub&gt; - dV&lt;sup&gt;-&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Manipulation of the light reading is performed only if and when either of the dV limits are exceeded. Otherwise the raw reading is accepted as is. &lt;br /&gt;&lt;br /&gt;From a &lt;a href="https://spreadsheets.google.com/spreadsheet/ccc?key=0AuWFzMAGPO9vdHZYWFhDNUh5Sk5NOTBJMmNQNUZtNUE&amp;amp;hl=en_US"&gt;record of ambient light levels&lt;/a&gt; during dawn and dusk we can determine that the maximum dV/dt = ~4V / 15min = ~4.5mV/s @V&lt;sub&gt;DD&lt;/sub&gt; = 5V. Therefore, assuming dt = 1s, we can let&amp;nbsp; dV&lt;sup&gt;+&lt;/sup&gt; equal to some value close to 4.5mV while&amp;nbsp; dV&lt;sup&gt;-&lt;/sup&gt; can be much larger in order to create the asymmetry. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To see how the MA and ADVA compare with one another I did a &lt;a href="https://spreadsheets.google.com/spreadsheet/ccc?key=0AuWFzMAGPO9vdEsyZ3BNNmRKcGRvaEdCWnd0Z2pJa3c&amp;amp;hl=en_US"&gt;simulation using a spreadsheet&lt;/a&gt;. Check it out to see the numerical values used. Below I've pasted the graphs from the simulation. For the MA the number of readings averaged = 16. For the ADVA, dV&lt;sup&gt;+&lt;/sup&gt; = 0.01V,  dV&lt;sup&gt;-&lt;/sup&gt; = 0.25V. These dV values are the ones I'm currently using in a bench test. &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-MVEkHsyq5ng/TihUfF7n6eI/AAAAAAAAAro/XO2NMGRj8Sc/s1600/anls%2Bsquare%2Bpositive.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="222" src="http://2.bp.blogspot.com/-MVEkHsyq5ng/TihUfF7n6eI/AAAAAAAAAro/XO2NMGRj8Sc/s400/anls%2Bsquare%2Bpositive.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The above simulates night condition where a transient bright light condition occurs (eg. a flashlight is shone directly at the sensor for many seconds). The light level is the blue rectangular pulse. The red curve is the MA response, while the yellow-orange curve is the ADVA output. Compared to the ADVA the MA has a much steeper slope. Because the MA averages 16 readings, after a time interval of 16, the MA output = to the sensor output. The ADVA on the other hand barely budges from its initial value. &lt;br /&gt;&lt;br /&gt;Now let's say the sensor output voltage at which the load is turned on is 1V and the hysteresis band = 1V (and so the load turn-off voltage = 2V). With the MA the load will be turned on and then off with this particular burst of light on the sensor. The ADVA meanwhile will be immune to it. For the transient to get the ADVA to trip the load, it has to be sustained for a much much longer time. &lt;br /&gt;&lt;br /&gt;Note the MA and ADVA waveforms: triangular and sawtooth, respectively. As we will see the waveforms will change depending on the input signal. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-OobeRm7vRBc/TihUfZ-h5uI/AAAAAAAAArw/Go3lU6ZsLjY/s1600/anls%2Bsquare%2Bnegative.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="235" src="http://1.bp.blogspot.com/-OobeRm7vRBc/TihUfZ-h5uI/AAAAAAAAArw/Go3lU6ZsLjY/s400/anls%2Bsquare%2Bnegative.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The above simulates daytime condition with the sensor suddenly immersed in darkness (eg. it was covered with a thick black plastic bag). The results are interesting. The MA and ADVA overlap each other during the downward slope. But this is just a coincidence. For a different number of readings for the MA or a different&amp;nbsp; dV&lt;sup&gt;-&lt;/sup&gt;, each of them will have a different negative slope. Nevertheless, with either filter the load will be turned on (assuming, as before that the trip levels are 1V and 2V). But when the light level returns to "normal," you can see how the ADVA takes its time rising. This means that it will be a very long while (about a hundred seconds in an actual bench test with dt = 1sec and  dV&lt;sup&gt;+&lt;/sup&gt; and&amp;nbsp; dV&lt;sup&gt;-&lt;/sup&gt; as per simulation values) before the load is switched off.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-X0cX4h6iaHY/TihUfUNJ53I/AAAAAAAAAr4/CQqnIWVk2ow/s1600/anls%2Btriangular%2Bpositive.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="225" src="http://1.bp.blogspot.com/-X0cX4h6iaHY/TihUfUNJ53I/AAAAAAAAAr4/CQqnIWVk2ow/s400/anls%2Btriangular%2Bpositive.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Any light level increase with a rate of change greater than those during dusk or dawn will be attenuated by the ADVA. What surprised me in the above is how a triangular input to the MA results in a (quasi?) Gaussian waveform.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Kkce56Mc8Vw/TihUfsGrf8I/AAAAAAAAAsA/2gYfgtgLqhw/s1600/anls%2Btriangular%2Bnegative.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="222" src="http://4.bp.blogspot.com/-Kkce56Mc8Vw/TihUfsGrf8I/AAAAAAAAAsA/2gYfgtgLqhw/s400/anls%2Btriangular%2Bnegative.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Because the rate of decrease in the light level above is less than dV&lt;sup&gt;-&lt;/sup&gt; the ADVA tracks the light level while it is decreasing and thus we see the waveforms overlapping. However, the two part ways when light level begins to increase. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-llyNIifPW3o/TihUf1Pf9dI/AAAAAAAAAsI/LzP-mEmUUWs/s1600/anls%2Bgeometric%2Bpositive.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="223" src="http://1.bp.blogspot.com/-llyNIifPW3o/TihUf1Pf9dI/AAAAAAAAAsI/LzP-mEmUUWs/s400/anls%2Bgeometric%2Bpositive.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Out of curiosity I tried out geometric changes in light level. The one above and below use a factor of 1.05. The ADVA tracks the increase in light level until the point where it exceeds&amp;nbsp; dV&lt;sup&gt;+&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-P9QA37BYdQc/TihUqsEdmjI/AAAAAAAAAsQ/tmOeJ0FsulE/s1600/anls%2Bgeometric%2Bnegative.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="222" src="http://2.bp.blogspot.com/-P9QA37BYdQc/TihUqsEdmjI/AAAAAAAAAsQ/tmOeJ0FsulE/s400/anls%2Bgeometric%2Bnegative.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Because the negative rate of change of the light level is well below dV&lt;sup&gt;-&lt;/sup&gt; the light level and ADVA waveforms overlap.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-7948169580260806331?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/7948169580260806331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/digital-filter-for-auto-night-light.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7948169580260806331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7948169580260806331'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/digital-filter-for-auto-night-light.html' title='Digital filter for auto night light'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-848l3JC5CJc/TihDTxnysiI/AAAAAAAAArg/73dlpmFrovQ/s72-c/anls%2Bsensor%2Banalog.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-8064674236210008634</id><published>2011-07-17T17:06:00.000+08:00</published><updated>2011-07-17T17:06:15.601+08:00</updated><title type='text'>New Youtube lectures on filters by Analog Devices</title><content type='html'>Assuming you know the basics of electronics, including op amps, the following lectures are easy to understand and pretty enlightening. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=ftk9C45F_bo"&gt;Filtering 101: Single Pole Filters &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=e9pYFb7PeJg"&gt;Filtering 101: Single Pole Filers with Op Amps&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=cH_GHbrNyUc"&gt;Filtering 101: Multi Pole Filters with Real Poles&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;F&lt;a href="http://www.youtube.com/watch?v=DEV7l66D6Ys"&gt;iltering 101: Multi Pole Filters with Sallen-Key &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-8064674236210008634?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/8064674236210008634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/new-youtube-lectures-on-filters-by.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8064674236210008634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8064674236210008634'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/new-youtube-lectures-on-filters-by.html' title='New Youtube lectures on filters by Analog Devices'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-8995723921214028915</id><published>2011-07-13T22:38:00.000+08:00</published><updated>2011-07-13T22:38:28.436+08:00</updated><title type='text'>Microcontroller-based automatic night light circuit</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-dxqxavYlsiQ/Th2alMDr6GI/AAAAAAAAArA/FVhHHIAYpPg/s1600/ANLS-PIC12F1822-schematic.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-dxqxavYlsiQ/Th2alMDr6GI/AAAAAAAAArA/FVhHHIAYpPg/s1600/ANLS-PIC12F1822-schematic.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-dxqxavYlsiQ/Th2alMDr6GI/AAAAAAAAArA/FVhHHIAYpPg/s1600/ANLS-PIC12F1822-schematic.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="211" src="http://3.bp.blogspot.com/-dxqxavYlsiQ/Th2alMDr6GI/AAAAAAAAArA/FVhHHIAYpPg/s320/ANLS-PIC12F1822-schematic.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;While discussing an &lt;a href="http://electromotiveforces.blogspot.com/2011/07/triac-optoisolator-directly-driving.html"&gt;all-linear automatic night light circuit&lt;/a&gt; I mentioned that I was testing an MCU-based ANLC. Well, I've been tweaking the firmware since. The other day I installed the sensor outdoors and have connected the circuit to control 220VAC loads.&lt;br /&gt;&lt;br /&gt;Last night the circuit got fooled when clouds moved in at around 2am and reflected sufficient city lights and streetlights that the MCU thought it was already the beginning of dawn and switched off the load. This is because the software algorithm is such that a 10-bit ADC change of just 0x00A from the average dark ambient light level is interpreted as dawn. Competent programmer that I am, the firmware also includes a conditional which turns the load back on again should the light level drop back to the average dark level. And some half hour or an hour later (not sure because I went back to sleep before waking up again some time later) it did actually did return to dark level conditions. A sonalert buzzer is temporarily hooked up such that it sounds when the load (perimeter security lights) is off and as far as I know (sleepy head that I am) the next time the load was switched off by the circuit was around 5am--true dawn. &lt;br /&gt;&lt;br /&gt;Earlier that same evening at around midnight while remotely monitoring the voltage output of the sensor ("ldr" in the schematic) with a DMM I was surprised to see the numbers climbing rapidly. The output had already settled at ~20mV but then increased quickly within minutes and peaked at ~310mV. I had to look out the window just to make sure I wasn't witnessing a circuit glitch but that in fact ambient light level was shooting up. And true enough the sky had turned orange and bright--from the cloud cover. The circuit did not turn the load off because the firmware was using a different hysteresis value at that time. Only after the load has been on for six hours does it use the hysteresis value of ten (the 10-bit ADC value mentioned above). By 2am this six hours had already elapsed.&lt;br /&gt;&lt;br /&gt;Because of the detection sensitivity used, the light sensor circuit is located on the roof to minimize being affected by artificial lights. The LDR faces directly up (zenith) and is housed in a translucent plastic box. The box is shielded on the sides to prevent streetlights and lights from nearby buildings and billboards from unduly affecting the sensor. The very high impedance output of the sensor requires a unity gain buffer. The LDR used has a resistance of &amp;gt;30Mohms in complete darkness.&lt;br /&gt;&lt;br /&gt;On the main control board, a low pass filter comprised of R&lt;sub&gt;LPF&lt;/sub&gt; and C&lt;sub&gt;LPF&lt;/sub&gt; (cut off frequency = 1/(2πRC) = 1/(6.28*100K*0.1uF) = 16Hz) attenuates high frequency noise which could be caused by EMI particularly those from lightning. A unity gain buffer is used to provide a low impedance signal for the MCU. High value load resistor R&lt;sub&gt;L&lt;/sub&gt; is used because in the event that the cable to the sensor breaks, the input of the LPF and buffer will not be left floating. In such an event R&lt;sub&gt;L&lt;/sub&gt; grounds the input, fooling the MCU to think that it's nighttime and thus turning on the load. This serves to alert the user (in the daytime) of a malfunction.&lt;br /&gt;&lt;br /&gt;PB is used to select the light level at which load will be turned on. The user waits until the ambient light level is at the point where s/he wants the load switched on. At that moment PB is pressed. The circuit stores the light level in EEPROM and uses this value until it is changed. Pressing and holding down PB for a second resets the value to its minimum (I arbitrarily set it ADC value 0x064)--meaning load will be turned on only when it's really dark. LY is the indicator light for PB. It flashes briefly when PB is pressed (falling edge detected) and it blinks on and off for 1.5s when the trip level has been reset to minimum. A debouncing routine is used to, well, debounce PB, detect falling/rising edges, and monitor how long PB has been depressed. &lt;br /&gt;&lt;br /&gt;REL1 and REL2 are mini relays with 24VAC coil. They are switched using a &lt;a href="http://electromotiveforces.blogspot.com/2011/07/triac-optoisolator-directly-driving.html"&gt;MOC3021 optoisolator employing a snubber circuit&lt;/a&gt;. REL1 and REL2 are at different locations. Using low voltage to control the relays translates to safety and perhaps even less expensive wires.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-8995723921214028915?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/8995723921214028915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/microcontroller-based-automatic-night.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8995723921214028915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8995723921214028915'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/microcontroller-based-automatic-night.html' title='Microcontroller-based automatic night light circuit'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-dxqxavYlsiQ/Th2alMDr6GI/AAAAAAAAArA/FVhHHIAYpPg/s72-c/ANLS-PIC12F1822-schematic.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-186327022925512598</id><published>2011-07-12T01:38:00.000+08:00</published><updated>2011-07-12T01:38:08.456+08:00</updated><title type='text'>Triac optoisolator directly driving an inductive load</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/--tLI8FghrJU/Thspe1eQVII/AAAAAAAAAqw/JiScbDUyKGo/s1600/moc3021.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="131" src="http://1.bp.blogspot.com/--tLI8FghrJU/Thspe1eQVII/AAAAAAAAAqw/JiScbDUyKGo/s200/moc3021.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;As far as I know the MOC301x and MOC302x series of triac optoisolators are intended to drive power triacs and not meant to drive loads directly. Indeed, I just checked &lt;a href="http://www.fairchildsemi.com/ds/MO/MOC3021M.pdf"&gt;Fairchild's datasheet&lt;/a&gt; and it says so explicitly: "The optoisolator should not be used to drive a load directly. It is intended to be a trigger device only." But then Motorola, I mean On Semiconductor, tells us that, "Zero-crossing optocouplers can be used to switch inductive loads at currents less than 100 mA (Figure 24)" [&lt;a href="http://www.onsemi.com/pub/Collateral/HBD855-D.PDF"&gt;p.162&lt;/a&gt;].&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-9mJG8Ne-yr8/Thsg-N35OTI/AAAAAAAAAqo/gfamguBrfFc/s1600/MOC3062%2Binductive%2Bload%2Bcurrent%2Band%2Bsnubber.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="347" src="http://3.bp.blogspot.com/-9mJG8Ne-yr8/Thsg-N35OTI/AAAAAAAAAqo/gfamguBrfFc/s400/MOC3062%2Binductive%2Bload%2Bcurrent%2Band%2Bsnubber.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;What I have in my parts bin are a number of MOC3021. Since these can pass around 100mA [&lt;a href="http://www.onsemi.com/pub/Collateral/HBD855-D.PDF"&gt;p.124&lt;/a&gt;] continuously, with a peak current of 1000mA (pulse width of 1ms, 120 pulses per second), there shouldn't be a problem if the load does not exceed these maximums. The MOC3021 is a random-phase and not zero-crossing optoisolator but I figure if I just close my eyes, ignore the difference and wish upon a star then everything will be alright.&lt;br /&gt;&lt;br /&gt;The load I need to switch are &lt;a href="http://products.schneider-electric.us/products-services/product-detail/?event=productDetail&amp;amp;countryCode=us&amp;amp;partNumber=RXM2AB1B7"&gt;24VAC mini relays&lt;/a&gt; which in turn will switch 220VAC loads. Measured maximum current of the relay @24volts is 46mA. Connecting a 24VAC source and a relay in series with the triac output presented no problems--turning the MOC3021's LED on and off switched the relay. I then wondered if the optoisolator could drive two relays in parallel. They switched on fine, but when I powered down the LED the relays remained on. They just wouldn't turn off.&lt;br /&gt;&lt;br /&gt;To try and remedy this I placed a 0.1uF ceramic capacitor across the triac. That immediately got rid of the latching. I tweaked the snubber and replaced the cap with a 0.01uF and a 1Kohm 1/4W resistor. No latching problems. However, decreasing capacitance to 1nF or increasing resistance to tens of kilohms reduced snubbing to a point that latching would occur. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-NUlLhlU-R3E/ThssxnB7jFI/AAAAAAAAAq4/vPZCjlvvVx4/s1600/moc3021-relay-schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="272" src="http://3.bp.blogspot.com/-NUlLhlU-R3E/ThssxnB7jFI/AAAAAAAAAq4/vPZCjlvvVx4/s400/moc3021-relay-schematic.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The value of the snubbing resistor should probably be close to the resistance of the load. Given that the coil resistance of each relay is around 180ohms, snubbing resistor should be around 91 or 100ohms.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-186327022925512598?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/186327022925512598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/triac-optoisolator-directly-driving.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/186327022925512598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/186327022925512598'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/triac-optoisolator-directly-driving.html' title='Triac optoisolator directly driving an inductive load'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/--tLI8FghrJU/Thspe1eQVII/AAAAAAAAAqw/JiScbDUyKGo/s72-c/moc3021.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-8187980899511197041</id><published>2011-07-07T02:56:00.001+08:00</published><updated>2011-07-07T09:31:22.735+08:00</updated><title type='text'>Vexing silicon bug on the PIC12F1822</title><content type='html'>Been having a problem with a firmware which uses the analog-to-digital converter (ADC) of the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en544839"&gt;PIC12F1822&lt;/a&gt; to read the buffered voltage output of a sensor. The circuit works for some time but then minutes or hours later goes amok. I've gone over the firmware half a dozen times and I can't find anything wrong. (That of course is no guarantee that the program has no coding errors. I may just be "blind" to the booboos I've committed.)&amp;nbsp; &lt;br /&gt;&lt;br /&gt;While testing the circuit and observing the hiccups, it looked to me--among other possibilities--that the watchdog timer (WDT) was timing out--that it wasn't being cleared and thus the MCU was being reset. Sure enough when I disabled the WDT the MCU would hang and the push button and LED would fail to respond. Something was very wrong indeed. It was then that it occurred to me to check the &lt;a href="http://ww1.microchip.com/downloads/en/DeviceDoc/80502C.pdf"&gt;silicon errata sheet&lt;/a&gt; for this device. &lt;br /&gt;&lt;br /&gt;When I saw item number two on the rather obscenely long list of bugs I had an "Oh shoot!" moment. According to Microchip &lt;br /&gt;&lt;blockquote&gt;Under certain device operating conditions, the ADC conversion may not complete properly. When this occurs, the ADC Interrupt Flag (ADIF) does not get set, the GO/DONE bit does not get cleared and the conversion result does not get loaded into the ADRESH and ADRESL result registers.&lt;/blockquote&gt;Two workarounds are listed:&lt;br /&gt;&lt;blockquote&gt;Method 1: Select the dedicated RC oscillator as the ADC conversion clock source, and perform all conversions with the device in Sleep.&lt;br /&gt;&lt;br /&gt;Method 2: Provide a fixed delay in software to stop the A-to-D conversion manually, after all 10 bits are converted, but before the conversion would complete automatically. The conversion is stopped by clearing the GO/DONE bit in software. The GO/DONE bit must be cleared during the last ½ TAD cycle, before the conversion would have completed automatically. Refer to Figure 1 for details.&lt;/blockquote&gt;The firmware uses &lt;a href="http://www.mikroe.com/eng/products/view/7/mikroc-pro-for-pic/"&gt;mikroC&lt;/a&gt;'s built-in ADC function and checking the assembly of the compiled program it's clear that the function is polling the GO/DONE bit to determine whether conversion is finished or not. If the silicon bug is indeed being triggered then when it does strike this polling would go on ad infinitum since the bit would never change state. To find out if this ADC bug is the cause of my woes I had to write my own ADC function to implement one of the workarounds above. &lt;br /&gt;&lt;br /&gt;Well the part about clearing the GO/DONE bit during the last ½ TAD cycle in method 2 is a great put-off. I just don't like the idea of stopping a conversion at "just the right time." Seems too risky. So I opted for putting the MCU to sleep. Sounds simple and straightforward. Here's my version of Method 1. &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;unsigned int ADC(int8 ch)&lt;br /&gt;{&lt;br /&gt;  ADCON0 = ch &amp;lt;&amp;lt; 2;                    // left shift ADC channel to be read from into ADCON0&lt;br /&gt;  ADCON0.ADON = 1;                     // power up ADC&lt;br /&gt;  ADCON1 = 0b11110000;                 // right justified, use Frc, Vdd as positive reference voltage&lt;br /&gt;  INTCON.GIE = 0;                      // global interrupt disabled&lt;br /&gt;  PIR1.ADIF = 0;                       // clear ADC interrupt flag&lt;br /&gt;  PIE1.ADIE = 1;                       // ADC interrupt enabled&lt;br /&gt;  INTCON.PEIE = 1;                     // peripheral interrupts enabled&lt;br /&gt;  ADCON0.GO = 1;                       // start ADC conversion&lt;br /&gt;  asm {sleep}                          // zzzzzzzzzzzzzzz.... when ADIF = 1 MCU will wake up and will continue execution of the program&lt;br /&gt;  PIR1.ADIF = 0;                       // clear ADC interrupt flag&lt;br /&gt;  PIE1.ADIE = 0;                       // disable ADC interrupt&lt;br /&gt;  INTCON.PEIE = 0;                     // disable peripheral interrupts&lt;br /&gt;  ADCON0.ADON = 0;                     // power down ADC&lt;br /&gt;  return ADRESH*256 + ADRESL;          // return the 10-bit ADC value as a 16-bit number&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I've been testing the circuit using the above function instead of mikroC's and so far so good. WDT is enabled and so far no resets have occurred. Will do extended testing over the week to make sure the problem has been truly licked.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;[Addendum: Forgot to mention that, as per the errata sheet, the ADC bug appears only on 1822's with device revision ID#6. I did check the parts (see the errata for instructions on how to do this) I have before even attempting to write the workaround and of course as luck would have it, I had been shipped the affected components. Sigh.] &lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-8187980899511197041?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/8187980899511197041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/vexing-silicon-bug-on-pic12f1822.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8187980899511197041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/8187980899511197041'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/vexing-silicon-bug-on-pic12f1822.html' title='Vexing silicon bug on the PIC12F1822'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-3381537151390465488</id><published>2011-07-04T10:23:00.001+08:00</published><updated>2011-07-04T10:27:13.170+08:00</updated><title type='text'>Why hasn't Microchip fabricated IAs?</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://sub.allaboutcircuits.com/images/03046.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="118" src="http://sub.allaboutcircuits.com/images/03046.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;I only use op amps which are single supply and I practically don't use any other op amp except those by Microchip because they're cheap, easy to obtain, and a good number of them have rail to rail I/O. Well, right now I need a single-supply instrumentation amplifier to clean up possible common mode noise from a sensor that'll be located many meters away from the control board. No amplification is necessary or desired. Unfortunately the only IA I have on hand is the &lt;a href="http://focus.ti.com/docs/prod/folders/print/ina126.html"&gt;INA126&lt;/a&gt; which needs a bipolar supply (else output won't go all the way to ground) and has a minimum gain of 5.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/a/a2/Op-Amp_Differential_Amplifier.svg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="133" src="http://upload.wikimedia.org/wikipedia/commons/a/a2/Op-Amp_Differential_Amplifier.svg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;Because the input signal is low impedance I could just use a differential amplifier. I can cobble one up using, say, a &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010438"&gt;MCP6021&lt;/a&gt;, but that would entail the hassle of either getting precision resistors or manually matching four. It also increases the parts count. So that got me wondering why Microchip--given their wide range of op amps--hasn't taken the next step and developed instrumentation and difference amplifiers. Do they lack the capability to make these high precision components? Is the demand for these items too small? Is that market cornered by Analog Devices and Texas Instruments?&lt;br /&gt;&lt;br /&gt;It's still on my wishlist and hopefully Microchip does get around to fabricating IAs in the near future. I'm sure they'd be more affordable than, say, the &lt;a href="http://www.analog.com/en/specialty-amplifiers/instrumentation-amplifiers/ad623/products/product.html"&gt;AD623&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-3381537151390465488?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/3381537151390465488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/why-hasnt-microchip-fabricated-ias.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3381537151390465488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/3381537151390465488'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/why-hasnt-microchip-fabricated-ias.html' title='Why hasn&apos;t Microchip fabricated IAs?'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4333823499214976648</id><published>2011-07-03T22:01:00.002+08:00</published><updated>2011-07-03T22:04:14.849+08:00</updated><title type='text'>mikroC Pro v4.60 bug</title><content type='html'>Serendipitiously the mikroC Pro v5.00--successor to v4.60--was just released a couple of days ago. Because I ran into a brick wall programming the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en544839"&gt;PIC12F1822&lt;/a&gt; with the 4.60. The firmware compiled without a hitch for the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en027148"&gt;PIC12F615&lt;/a&gt; but then the compiler started going bananas when I changed the MCU to 12F1822 (had to change the MCU because it turns out I need EEPROM which unfortunately doesn't exist in the 12F615). After systematically commenting out various parts of the program to isolate what was tripping the compiler, I narrowed down the problem to a local variable that the 4.60 demanded be initialized, else it would give me a "362 Not enough RAM 'R15'  P12F1822.c" error message upon compilation. Here's an example. The following will result in an error:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;char  i;&lt;br /&gt;&lt;br /&gt;void Mozza()&lt;br /&gt;{&lt;br /&gt;  char i;&lt;br /&gt;  for (i=1; i&amp;lt;8; i++)  ;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  Mozza();&lt;br /&gt;  while(1) ;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;You can change &lt;code&gt;for (i=1; i&amp;lt;8; i++)&lt;/code&gt; to something else, like &lt;code&gt;PORTA += i; &lt;/code&gt; and the compiler will cough out the same error message. On the other hand, the following versions will compile properly:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;char  i;&lt;br /&gt;&lt;br /&gt;void Mozza()&lt;br /&gt;{&lt;br /&gt;  char i=0;    // no error if variable initialized&lt;br /&gt;  for (i=1; i&amp;lt;8; i++)  ;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  Mozza();&lt;br /&gt;  while(1) ;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;char  i;&lt;br /&gt;&lt;br /&gt;void Mozza()&lt;br /&gt;{&lt;br /&gt;  // char i;   no error if global variable is used&lt;br /&gt;  for (i=1; i&amp;lt;8; i++)  ;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  Mozza();&lt;br /&gt;  while(1) ;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;So using a global variable or initializing the local var should've been an easy workaround. Unfortunately, when mikroC's built-in EEPROM write functions are present even these tricks fail to lick the problem. &lt;br /&gt;&lt;br /&gt;I just installed v5.00 and they've apparently fixed this bug. The firmware (still a work in progress) has thus far compiled without the error 362 message popping up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4333823499214976648?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4333823499214976648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/mikroc-pro-v460-bug.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4333823499214976648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4333823499214976648'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/07/mikroc-pro-v460-bug.html' title='mikroC Pro v4.60 bug'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4105454007445574097</id><published>2011-06-29T23:59:00.002+08:00</published><updated>2011-07-03T22:50:31.729+08:00</updated><title type='text'>All-analog automatic night light circuit</title><content type='html'>Some ten days ago I installed an all-linear automatic night light circuit to control the lights inside the living room. Circuit consists of three op amps--two used as voltage followers and a third as a comparator. As the schematic shows, all user adjustable controls are at the comparator--with one potentiometer to set the trip point and the other pot to adjust the width of hysteresis band.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rGrqW1I1rTU/ThCBi906xPI/AAAAAAAAAqg/dvr7QZwTacE/s1600/ANLS-analog-schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="239" src="http://4.bp.blogspot.com/-rGrqW1I1rTU/ThCBi906xPI/AAAAAAAAAqg/dvr7QZwTacE/s400/ANLS-analog-schematic.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The sensor circuit is located several meters away at a location where it can have a view of the outdoors without being blinded or fooled by indoor lights at night. The high impedance sensor and low pass filter must have a unity gain buffer (voltage follower) within millimeters/centimeters. You can't have it on the main control circuit board meters away. Trust me. I've tested this several years ago with and without a buffer and a (unshielded twisted pair) cable. The signal will be degraded and probably unusable without the follower. Notice the low pass filter includes a diode. It&amp;nbsp; performs a very important and essential function. See my &lt;a href="http://electromotiveforces.blogspot.com/2011/05/automatic-night-light-circuits.html"&gt;other automatic night light circuits&lt;/a&gt; for the explanation.&lt;br /&gt;&lt;br /&gt;A 3-wire cable connects the sensor circuit to the main control circuit. According to the &lt;a href="http://ww1.microchip.com/downloads/en/DeviceDoc/21810f.pdf"&gt;MCP627x op amp datasheet&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Driving large capacitive loads can cause stability problems for voltage feedback op amps. As the load capacitance increases, the feedback loop’s phase margin decreases and the closed-loop bandwidth is reduced. This produces gain peaking in the frequency response, with overshoot and ringing in the step response. A unity-gain buffer (G = +1) is the most sensitive to capacitive loads, though all gains show the same general behavior. When driving large capacitive loads with these op amps (e.g., &amp;gt; 100 pF when G = +1), a small series resistor at the output improves the feedback loop’s phase margin (stability) by making the output load resistive at higher frequencies. (DS21810F, 2008, p.14)&lt;/blockquote&gt;&lt;br /&gt;Well, we're working with essentially a DC signal and I have no idea of how much capacitance there is in the cable, but I just added R3 to be sure. The hysteresis circuit (R5, R6, R7) needs a low impedance source, thus A2 is used as a voltage follower--else R3 will foul up the resistor values for the hysteresis. For a discussion of hysteresis in single-supply op amps &lt;a href="http://electromotiveforces.blogspot.com/2010/09/adjustable-power-supply-with-automatic.html"&gt;read this&lt;/a&gt;. Equations are provided. &lt;br /&gt;&lt;br /&gt;A &lt;a href="http://electromotiveforces.blogspot.com/2011/05/push-pull-output-using-complementary.html"&gt;push-pull configuration using complementary transistors&lt;/a&gt; drives the relay and LEDs. A 12-volt relay is the output amplifier (and high-voltage isolator) which switches the 220VAC load. The relay is some 10 ten meters from the control circuit. Its normally open contacts are connected in parallel with the light's wall switch--so this switch has to be off for the circuit to turn the load on/off automatically. Green and red indicator LEDs provide feedback when adjusting the pots.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The shortcoming of this circuit--and for that matter all ANL circuits--is that the ambient light level at which the load is turned &lt;i&gt;off&lt;/i&gt; must be higher than the light level at which it's switched &lt;i&gt;on&lt;/i&gt;. In other words, incorporating a&amp;nbsp; hysteresis--however small--forces the circuit to turn the load off at a brighter condition than for load switch-on.&lt;br /&gt;&lt;br /&gt;But in some cases (probably a majority) we want the lights off at the crack of dawn--i.e., at a darker condition than when it was turned on. Someone may have already done so, but I haven't yet come up with a way of doing this using linear components/ICs. I don't even know where to begin! Using a microcontroller on the other hand seems obvious and the simplest way to implement it. &lt;br /&gt;&lt;br /&gt;And that's what I've been doing these past two days. Because it's MCU-based the circuit itself is a no-brainer. The firmware's where the brain-racking is. I've already installed a temporary circuit in place of the above all-analog one and will be checking its performance till week's end.&lt;br /&gt;&lt;br /&gt;Given that I'm using an MCU--and all the computing power it's got--I pared down the sensor subcircuit and eliminated the low pass filter. Filtering is now performed digitally using algorithms. Pots are gone as well. Trip point is set using a momentary contact push button. "Negative" hysteresis (as I'm calling it, at least for now) is relatively simple to implement in firmware. Testing over the next few days will actually be assessment of the robustness of the algorithms I'm using, tweaking the values used in the equations, and ferreting out bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4105454007445574097?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4105454007445574097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/all-analog-automatic-night-light.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4105454007445574097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4105454007445574097'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/all-analog-automatic-night-light.html' title='All-analog automatic night light circuit'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-rGrqW1I1rTU/ThCBi906xPI/AAAAAAAAAqg/dvr7QZwTacE/s72-c/ANLS-analog-schematic.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-5003900595603850907</id><published>2011-06-21T10:39:00.004+08:00</published><updated>2011-06-21T11:11:20.350+08:00</updated><title type='text'>Using a photoelectric sensor to turn on hallway lights</title><content type='html'>There's a hallway in one part of the house where there are no windows around. That area gets really pitch black at night. The ceiling lights are normally off to save on power. Because I have to use that corridor several times at night I have to switch the lights on. Unfortunately the switch is some 5 meters from the room I'm going in and out of, so what happens is I'd turn on the lights, enter the room, switch the lights in there, and then go back out to turn off the lights outside, then go back in again. Obviously a hassle. Worst part of it is when my hands are full with stuff. I'd have to free one hand to flip the switch in the corridor.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rDHUVZ5lJ_A/Tf9FcmHEElI/AAAAAAAAApg/IhC3qbiZ4Dc/s1600/pz_51l_200px.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-rDHUVZ5lJ_A/Tf9FcmHEElI/AAAAAAAAApg/IhC3qbiZ4Dc/s400/pz_51l_200px.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;So ten years ago I installed a circuit that would turn the hallway lights on whenever I crossed a photocell. After about half a minute later it would automatically turn the lights off. I think it was in that year that I got hold of some Keyence photoelectric sensors that were on bargain so this was an application it was quite suited to. I was able to purchase several types, but the model I eventually used for the circuit was a &lt;a href="http://www.keyence.co.jp/switch/kouden/pz/pz_51l"&gt;Keyence PZ-51L&lt;/a&gt; (sorry, can't find a link in English). It's an infrared (IR) transmitter (TX) and receiver (RX) pair meant to be used in a &lt;i&gt;throughbeam&lt;/i&gt; configuration--i.e., TX and RX are placed some distance apart and facing one another such that TX IR beam is aimed directly at RX.&lt;br /&gt;&lt;br /&gt;The PZ-51L has an open NPN collector output (actually two, the other is for "alarm" purposes). Depending on how the "output mode control" (pink wire) of RX is connected--tied to ground or Vdd--the sensor could be made to switch the open collector NPN on when RX senses the IR beam (no object detected) or when it fails to (object is blocking the beam). In all the circuits below the PZ-51L is used in the "light-on" mode, meaning the NPN is switched on when RX detects the IR beam.&lt;br /&gt;&lt;br /&gt;Here are some pertinent specs, schematics, and diagrams lifted off a Keyence pdf catalog (came in a CD-ROM).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-VkLPkEhQwbo/Tf9INo7AJMI/AAAAAAAAApw/aTvxqrFn7Dw/s1600/keyence%2BPZ-51L%2Bdiagram%2B4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="286" src="http://4.bp.blogspot.com/-VkLPkEhQwbo/Tf9INo7AJMI/AAAAAAAAApw/aTvxqrFn7Dw/s400/keyence%2BPZ-51L%2Bdiagram%2B4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-9zTNVHV0Yy8/Tf9INoDDTvI/AAAAAAAAAp4/JP617n3vLPE/s1600/keyence%2BPZ-51L%2Bdiagram%2B1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="301" src="http://3.bp.blogspot.com/-9zTNVHV0Yy8/Tf9INoDDTvI/AAAAAAAAAp4/JP617n3vLPE/s400/keyence%2BPZ-51L%2Bdiagram%2B1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-yulwYD-jNo0/Tf9IN0B93cI/AAAAAAAAAqA/VSa53VqFMGk/s1600/keyence%2BPZ-51L%2Bdiagram%2B2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="317" src="http://2.bp.blogspot.com/-yulwYD-jNo0/Tf9IN0B93cI/AAAAAAAAAqA/VSa53VqFMGk/s400/keyence%2BPZ-51L%2Bdiagram%2B2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-b0qOiwNNyzY/Tf9INYhALaI/AAAAAAAAApo/CUNdKWHCR6Y/s1600/keyence%2BPZ-51L%2Bdiagram%2B3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-b0qOiwNNyzY/Tf9INYhALaI/AAAAAAAAApo/CUNdKWHCR6Y/s400/keyence%2BPZ-51L%2Bdiagram%2B3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I initially intended to use the sensor in a throughbeam configuration, but to simplify installation and do away with alignment problems I decided to use it &lt;i&gt;retroreflectively&lt;/i&gt;--i.e., TX and RX are side by side facing the same direction with RX sensing the reflection of the TX IR beam. Because the intensity of the light is much weaker in retroreflective mode the detection distance is much shorter than in throughbeam mode.&lt;br /&gt;&lt;br /&gt;The PZ-51L TX and RX were placed side by side and glued together using  double-sided foam tape. The pair was then just stuck using foam tape again to the jamb in the  hallway about a meter off the floor. Anyone crossing would intercept the  IR beam and reflect part of it back towards RX thus triggering it. There's a "SENS" trimmer on top  of RX used to control its sensitivity. This had to be set to  maximum to have RX detect people crossing. Even at maximum, however, sense distance was less than a meter. Type of clothing material may  have an effect on how well the person is detected.&lt;br /&gt;&lt;br /&gt;The initial circuit I developed back in 2001 was based on an &lt;a href="http://www.national.com/mpf/LM/LM311.html"&gt;LM311&lt;/a&gt; comparator. I'm not going to put up the schematic since it's close to being a most incompetent design and so don't want anyone using it. It didn't even have a voltage regulator IC. Suffice to say it used RC timing (to provide seconds of time delay and keep the lights on) and a voltage reference to let the comparator know when to switch its output. Comparator output was connected directly to a 12-volt relay--I wouldn't design something like that now, but back then I just copied from an electronics textbook which showed just that. I didn't have a&lt;a href="http://en.wikipedia.org/wiki/Flyback_diode"&gt; flyback diode&lt;/a&gt; across the relay coil--yet another faulty design. The relay then switched the lights on and off. Even with all the bad design elements, however, the circuit worked. I'd probably freak out now if I hooked up an oscilloscope to it. &lt;br /&gt;&lt;br /&gt;The next year I scrapped the circuit--leaving the PZ-51L as is--and installed something decent. It was based on the &lt;a href="http://en.wikipedia.org/wiki/555_timer_IC"&gt;555 timer&lt;/a&gt; in a &lt;a href="http://en.wikipedia.org/wiki/555_timer_IC#Monostable"&gt;monostable mode&lt;/a&gt;. A pull-up resistor was connected to RX's "control" NPN collector (black wire). And that collector output was tied to the 555's pin2. So whenever the NPN was switched on (object detected) output of the 555 went high for a duration determined by RC. Instead of a relay, a MOC3021 optoisolator was used to trigger a  Teccor Q4004L4 triac which then switched the lights. &lt;br /&gt;&lt;br /&gt;The following year (2003) I completely changed the circuit again, using a CD4023 triple 3-input NAND with two of them wired up a simple RS flip flop, a 555 in &lt;a href="http://en.wikipedia.org/wiki/555_timer_IC#Astable"&gt;astable configuration&lt;/a&gt; to provide a 16Hz output, and a CD4040 ripple counter which was clocked by the 555. I then NANDed&amp;nbsp; three counter outputs to obtain a desired time duration. I also included a sonalert buzzer which was switched on (via an NPN) by one of the counter outputs. This buzzer sounded only during the last few seconds of the countdown thus alerting the user that darkness was about to descend upon him. In 2004 I made  further (and final) revisions ending up with the following circuit which I will describe in detail.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-o0x50HxIwWs/Tf85_L3MPYI/AAAAAAAAApY/OA8QmaB6JBw/s1600/photoelectric-sensor-schema.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="94" src="http://2.bp.blogspot.com/-o0x50HxIwWs/Tf85_L3MPYI/AAAAAAAAApY/OA8QmaB6JBw/s400/photoelectric-sensor-schema.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;CNT pin14 count = 512&lt;br /&gt;CNT pin2&amp;nbsp; count = 32&lt;br /&gt;CNT pin3&amp;nbsp; count = 16&lt;br /&gt;&lt;br /&gt;R&lt;sub&gt;A&lt;/sub&gt; = 30Kohms&lt;br /&gt;R&lt;sub&gt;B&lt;/sub&gt; = 30K&lt;br /&gt;C&lt;sub&gt;T&lt;/sub&gt; = 1uF tantalum&lt;br /&gt;C&lt;sub&gt;F&lt;/sub&gt; = 1uF aluminum electrolytic&lt;br /&gt;&lt;br /&gt;PS = "wallwart" DC power supply, unregulated, no load output: 16VDC&lt;br /&gt;VR = LM7812 +12V 1000mA voltage regulator &lt;br /&gt;P-T = Keyence PZ-51L transmitter&lt;br /&gt;P-R = Keyence PZ-51L receiver&lt;br /&gt;TIM = NE556 dual timer&lt;br /&gt;CNT = CD4040 ripple counter&lt;br /&gt;NAND = CD4023 triple 3-input NAND gate&lt;br /&gt;Q&lt;sub&gt;B&lt;/sub&gt; =&amp;nbsp; S9013 NPN transistor I&lt;sub&gt;Cmax&lt;/sub&gt; = 500mA, h&lt;sub&gt;FE-min&lt;/sub&gt; = 60&lt;br /&gt;Q&lt;sub&gt;R&lt;/sub&gt; =&amp;nbsp; S9015 PNP transistor I&lt;sub&gt;Cmax&lt;/sub&gt; = 100mA, h&lt;sub&gt;FE-min&lt;/sub&gt; = 100&lt;br /&gt;D1, D2 = 1N4148 signal diode&lt;br /&gt;BUZ = sonalert buzzer&lt;br /&gt;OPTO = MOC3021 optoisolator with triac driver output&lt;br /&gt;TRIAC = Teccor Q4004L4 triac 400V 4A&lt;br /&gt;LSW = wall switch of light&lt;br /&gt;L = lamps, used to be incandescent, now CFL.&lt;br /&gt;&lt;br /&gt;all resistors are 1/4W 5% tolerance&lt;br /&gt;&lt;br /&gt;Here's how the circuit operates. When an object reflects the infrared beam and RX detects it ights will be immediately turned on. Simultaneously the buzzer will sound for around 2 milliseconds to give an audible feedback that the system has been activated. After around 32 seconds the buzzer will sound again, softly--due to the 30K resistor in series with BUZ and D1--for about 3 seconds, after which both lights and buzzer will be turned off. If a person crosses the IR beam while the lights are already on, then the counter will be reset and will begin counting half a minute of time again as soon as the person is no longer within the detection zone. If a person remains stationary and reflects the IR beam continuously the counter will remain reset and the lights will remain on indefinitely. &lt;br /&gt;&lt;br /&gt;TIM.a is wired in astable mode and provides a continuous 16Hz square wave for CNT clock input. Period of 555 is given by:&lt;br /&gt;&lt;br /&gt;T = 0.693(R&lt;sub&gt;A&lt;/sub&gt; + 2R&lt;sub&gt;B&lt;/sub&gt;)C&lt;sub&gt;T&lt;/sub&gt;&lt;br /&gt;&lt;br /&gt;where:&lt;br /&gt;T = period, in seconds&lt;br /&gt;R&lt;sub&gt;A&lt;/sub&gt; = resistance, in megohms&lt;br /&gt;R&lt;sub&gt;B&lt;/sub&gt; = resistance, in megohms&lt;br /&gt;C&lt;sub&gt;T&lt;/sub&gt; = capacitance, in mircofarads&lt;br /&gt;&lt;br /&gt;Using a 1uF tantalum capacitor and resistors R&lt;sub&gt;A&lt;/sub&gt;&amp;nbsp; =&amp;nbsp; R&lt;sub&gt;B&lt;/sub&gt; = 30Kohms gives a period of 0.06237 sec, a very good approximation of 1/16 sec. By counting multiples of 16 we can thus make the counter time in seconds. (Note: These are theoretical figures. Back then I didn't have a scope nor a any meter that could measure frequency or even capacitance to determine the actual output frequency of the 555.)&lt;br /&gt;&lt;br /&gt;NAND.s and NAND.r form a simple RS latch. Inputs to this latch must normally be high. When input goes low that particular gate's output will go high. C&lt;sub&gt;F&lt;/sub&gt; is a filter capacitor to prevent line noise from falsely triggering the latch.&lt;br /&gt;&lt;br /&gt;When P-R is activated, its internal NPN transistor is turned on.  NAND.s input is therefore brought low. If NAND.s is outputting a  low it will be triggered to output a high.  If NAND.s output is already high there is no change and no harm done. When NAND.s output is high lights are turned on. &lt;br /&gt;&lt;br /&gt;Buzzer will begin sounding when CNT count reaches 512. NAND.rst output will go low when CNT reaches 560 (or around 35 seconds), triggering NAND.r to output high which then resets CNT and switches off BUZ. Simultaneously, NAND.s output will go low, turning off the lights.&lt;br /&gt;&lt;br /&gt;The CNT outputs which are connected to NAND.rst inputs are 2&lt;sup&gt;10&lt;/sup&gt;=512, 2&lt;sup&gt;6&lt;/sup&gt;=32, and 2&lt;sup&gt;5&lt;/sup&gt;=16, or a total of 560 counts. Because the clock is running at 16Hz, 560 counts translates into 560 / 16 = 35 sec. When count reaches 512, pin 14 of CNT goes high. This turns on BUZ. Because CNT has limited drive capability and since volume of BUZ doesn't need to be high for this alert a 30K resistor is placed in series. Three seconds later, when count reaches 560, NAND.rst output goes low. Pins 12 and 13 of NAND.r are pulled low which causes output of NAND.r to go high and output of NAND.s goes low. CNT is reset and remains reset. NAND.rst output then goes high again. The circuit then is at its beginning state once more. &lt;br /&gt;&lt;br /&gt;Because of the particular outputs of CNT used, the buzzer will be on for the last 3 seconds of the count. This will provide an audible signal when the lights (load) are about to go out. If the buzzer needs to be on for a second or two longer then CNT output 2&lt;sup&gt;7&lt;/sup&gt;=64 can be used instead. D1 and D2 allow BUZ to be used independently by CNT and TIM.b.&lt;br /&gt;&lt;br /&gt;Whenever P-R is activated--even if L is already on--CNT is reset to zero, thus extending the time L is on by another cycle (~35sec). This retriggerability is achieved by a transistor inverter (Q&lt;sub&gt;R&lt;/sub&gt;) and a voltage divider&amp;nbsp; (R&lt;sub&gt;r1&lt;/sub&gt; and  R&lt;sub&gt;r2&lt;/sub&gt;). When voltage across R&lt;sub&gt;r1&lt;/sub&gt; is high CNT reset pin11 will be high (&amp;gt;50% system voltage) thus causing CNT to reset. The values of R&lt;sub&gt;r1&lt;/sub&gt; and R&lt;sub&gt;r2&lt;/sub&gt; were chosen so that current sinking/sourcing by NAND.r is minimal and so that proper voltages will be present to reset CNT. When P-R is activated Q&lt;sub&gt;R&lt;/sub&gt; is turned on. Voltage  across R&lt;sub&gt;r1&lt;/sub&gt; goes from low to high. This then  causes CNT to be reset. If a person stands in front of P-R, CNT will be continuously in reset mode, thus preventing any count. L will therefore be on indefinitely. Here's a table to make all this more comprehensible.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-0s4UQ3mp7Zc/Tf-AecZhNwI/AAAAAAAAAqI/VS3SUNGuFFw/s1600/photoelectric-sensor-table.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://4.bp.blogspot.com/-0s4UQ3mp7Zc/Tf-AecZhNwI/AAAAAAAAAqI/VS3SUNGuFFw/s400/photoelectric-sensor-table.png" width="256" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;When P-R is activated C&lt;sub&gt;P&lt;/sub&gt; begins charging. At this instant TIM.b pin8  goes low which then starts TIM.b. C&lt;sub&gt;P&lt;/sub&gt; charges very rapidly and after it  has charged voltage across it stays high until P-R is activated again. TIM.b output goes high for just a couple of milliseconds (given R&lt;sub&gt;T2&lt;/sub&gt; = 220K and C&lt;sub&gt;T2&lt;/sub&gt; = 0.01uF, theoretical pulse width is 1.1RC = 2.4ms), producing a brief clicking sound. The sound serves as a nonobstrusive indication that P-R has been activated (or reactivated if lights are already on). To make certain that sound does not last longer than this very brief period, triggering of TIM.b makes use of capacitor coupling. Given C&lt;sub&gt;P&lt;/sub&gt; = 0.001uF and R&lt;sub&gt;P&lt;/sub&gt; = 390K the negative trigger pulse is around 0.4ms--much shorter (as it should be) than the monostable output pulse. No negative pulse will occur again unless P-R output goes high then low again. Thus, even if a person were to stand in front of P-T/ P-R indefinitely, BUZ will still sound for only 2ms. He has to move away and then back again to re-trigger TIM.b.&lt;br /&gt;&lt;br /&gt;The load is switched by a power triac which in turn is triggered by a triac output optoisolator. No snubbing circuit is necessary. OPTO needs 15mA to ensure proper turn-on of its LED. Q&lt;sub&gt;B&lt;/sub&gt; has a minimum current gain of 60 and a V&lt;sub&gt;BE&lt;/sub&gt; of 0.7V. Maximum base resistor should therfore be around 45K. A 20K resistor was chosen to ensure more than enough collector current will be available. The actual current delivered to the LED of OPTO is determined by the current limiting resistor in series with the load. In this case the 470 ohm provides about 21mA.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Except for the AC adapter that's plugged into the outlet and the PZ-51L, the entire circuit is in a small box that's just glued to the concrete wall. The thing is, just a few days ago I needed to add a circuit--for a totally different application--in that box, one that will be using the same power supply. Trouble was there wasn't enough space. So even as I didn't wish to retire the above circuit I was forced to.&lt;br /&gt;&lt;br /&gt;Given what the above circuit does--how simple the requirements are--a single "primitive" microcontroller suffices to replace all the ICs and practically all the discretes, thus freeing up more than enough space. Thus the latest version of the photoelectric sensor circuit was born.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-yUlKwgYxR38/Tf-CIAeldxI/AAAAAAAAAqQ/NLpJEiG6rxY/s1600/photoelectric-sensor-2011june.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="http://3.bp.blogspot.com/-yUlKwgYxR38/Tf-CIAeldxI/AAAAAAAAAqQ/NLpJEiG6rxY/s400/photoelectric-sensor-2011june.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;MCU is a Microchip &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en020030"&gt;PIC10F202&lt;/a&gt;. Internal weak pull-up is enabled so P-R NPN collector can be tied to the MCU's pin (configured in firmware as input of course) without an external pull-up resistor. The transistor at output GP2 is a 2N7000 MOSFET. &lt;br /&gt;&lt;br /&gt;Operation is similar to the preceding circuit. In fact I tried to mimic everything about it. When sensor first detects an object a click is sounded, the load is turned on, and countdown begins. The "click" is simply to alert user that a (re)detection has occurred and that countdown has been started/reset. If object remains within the detection zone no further clicks are sounded and countdown will not commence until object has left the zone. Once countdown has commenced and before it has ended and an object again crosses the zone, countdown is canceled and reset and a click is sounded. Three seconds before countdown ends a series of clicks are sounded until countdown ends. The three-second series of clicks warns the user of the pending lights switch-off. When countdown ends, buzzer and lights are switched off. &lt;br /&gt;&lt;br /&gt;The only noticeable difference to the user between this and the previous circuit is the replacement of the continuous buzzer sound with a series of clicks during the last three seconds of countdown. The change is due to the fact that I needed to attenuate the buzzer volume. Placing a 10K resistor in series with BUZ lowered the volume of the last-three-second alert alright. But it also made the clicks (when object is first detected) barely audible. I toyed around with toggling BUZ every timer0 tick--on for one tick and off during the next, effectively doing a pulse width modulation at 50Hz with a 50% duty cycle. Didn't sound good. Kind of grating actually. So after a little experimentation I decided on a series of 10ms clicks spaced 200ms apart (5% duty cycle). This by the way is the same audible alert pattern I used in a &lt;a href="http://electromotiveforces.blogspot.com/2010/09/pantry-light-switch-using-pir-module.html"&gt;motion detection circuit&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I won't go into the details of the firmware since it's short and relatively simple. There should be enough comments therein to make the whole thing easy to comprehend. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt;&lt;br /&gt;Photoelectric timed switch&lt;br /&gt;June 19-20 2011&lt;br /&gt;Edwardson Tan&lt;br /&gt;&lt;br /&gt;processor = PIC10F202&lt;br /&gt;compiler = Hi-Tech C Lite v9.80&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;htc.h&gt;&lt;htc.h&gt;&lt;br /&gt;&lt;br /&gt;__CONFIG (WDTEN &amp;amp; UNPROTECT &amp;amp; MCLRDIS);          // config if using Hi-Tech C v9.80&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ========================================================================================================&lt;br /&gt;// values for the following defines can be modified by user to suit the application&lt;br /&gt;#define  loadonms            30000               // how long lights will be on, in milliseconds. this is the countdown period&lt;br /&gt;#define  alertms             3000                // how long last-few-seconds alert will sound right before load is switched off, in milliseconds&lt;br /&gt;#define  clickintervalms     200                 // duration between clicks for last-few-seconds alert, in milliseconds &lt;br /&gt;// ========================================================================================================&lt;br /&gt;&lt;br /&gt;#define  t0end               39                  // end value of timer0 to signal "overflow". used in conditional statement "if (TMR0 == t0end)"&lt;br /&gt;#define  t0tick              10                  // rounded-off timer0 tick in milliseconds = t0end * prescale * 1e-6sec * 1000 = (39)(256)/(1000) = 9.984ms &lt;br /&gt;#define  timetilloadoff      loadonms/t0tick                         // number of t0ticks before lights are switched off. this is the countdown period&lt;br /&gt;#define  timetilbeep         timetilloadoff - alertms/t0tick         // number of t0ticks before 3-second beep starts sounding to alert user that lights are about to be turned off &lt;br /&gt;#define  clickinterval       clickintervalms/t0tick                  // duration of last-few-seconds alert in terms of number of t0ticks &lt;br /&gt;&lt;br /&gt;#define  sw                  GP0                 // open collector output of Keyence PZ-51L IR receiver NPN&lt;br /&gt;#define  buz                 GP1                 // sonalert buzzer 1" diameter&lt;br /&gt;#define  load                GP2                 // 2N7000 switches MOC3021 which switches Q4004L4 triac which switches load&lt;br /&gt;&lt;br /&gt;#define  int1                bit&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;#define  yes                 1&lt;br /&gt;#define  no                  0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ========================================================================================================&lt;br /&gt; &lt;br /&gt;int1     reset;              // when writing to this flag: 1 = during current t0 tick sw is high (no object in detection zone);  0 = during current t0tick sw is low (object in detection zone)&lt;br /&gt;                             // when reading this flag: 1 = during the last t0 tick, sw was high (no object detected);  0 = during the last t0 tick sw was low (object was in detection zone)&lt;br /&gt;int1     countdown;          // 1 = countdown to load switch-off is underway; 0 = countdown not ongoing&lt;br /&gt;int8     COUNT;              // records how many t0 ticks from the last beep. used only during last-few-seconds alert. this variable cleared whenever sw is low&lt;br /&gt;int16    ELAPSEDTIME;        // records how many t0 ticks have elapsed&lt;br /&gt;&lt;br /&gt;// ========================================================================================================&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  TRIS = 0x1;&lt;br /&gt;  GPIO = off;&lt;br /&gt;  OPTION = 0b10000111;                 // wake up on change off, weak pull ups on, timer0 prescaler = 1:256 &lt;br /&gt;  TMR0 = 0;&lt;br /&gt;  reset = off;&lt;br /&gt;  countdown = off;&lt;br /&gt;  &lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (TMR0 &amp;gt;= t0end)&lt;br /&gt;    {&lt;br /&gt;      TMR0 = 0;&lt;br /&gt;      CLRWDT();                        // according to datasheet WDT nomimal timeout is 18ms&lt;br /&gt;      buz = off;                       // buzzer is turned off every timer0 tick. Thus all beeps last only a maximum of one timer0 tick. Each beep will sound more like a click than a beep&lt;br /&gt;&lt;br /&gt;      if (sw == hi)                    // sw is high when no presence is detected &lt;br /&gt;      {&lt;br /&gt;        reset = yes;&lt;br /&gt;        if (countdown)&lt;br /&gt;        {&lt;br /&gt;          ++ELAPSEDTIME;&lt;br /&gt;          if (ELAPSEDTIME &amp;gt;= timetilbeep)&lt;br /&gt;          {&lt;br /&gt;            if (++COUNT &amp;gt;= clickinterval)&lt;br /&gt;            {&lt;br /&gt;              buz = on;                // last-few-seconds alert before light is turned off. consists of 10ms beeps every clickinterval   &lt;br /&gt;              COUNT = 0;&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;          if (ELAPSEDTIME &amp;gt;= timetilloadoff)     // countdown ended so turned off buzzer, load, and clear countdown flag &lt;br /&gt;          {&lt;br /&gt;            buz = off;&lt;br /&gt;            load = off;&lt;br /&gt;            countdown = off;&lt;br /&gt;          }      &lt;br /&gt;        } // if (countdown)&lt;br /&gt;      } // if (sw == hi)&lt;br /&gt;      else                             // sw is low when object is in detection zone  &lt;br /&gt;      {&lt;br /&gt;        if (reset)                     // if NPN collector was high during the previous checks then turn on buzzer now since object has been detected. buzzer will be turned off during the next timer0 tick. Therefore this beep will last only for a max of 10ms, thus sounding more like a click than a beep. if NPN collector had been low then it means object has not moved out of the field of detection, so do not issue a click&lt;br /&gt;          buz = on;&lt;br /&gt;        load = on;&lt;br /&gt;        reset = off;  &lt;br /&gt;        countdown = on;&lt;br /&gt;        ELAPSEDTIME = 0;&lt;br /&gt;        COUNT = 0; &lt;br /&gt;      } // if sw == lo&lt;br /&gt;    } // if (TMR0 &amp;gt;= t0end)      &lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/htc.h&gt;&lt;/htc.h&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-5003900595603850907?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/5003900595603850907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/using-photoelectric-sensor-to-turn-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5003900595603850907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5003900595603850907'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/using-photoelectric-sensor-to-turn-on.html' title='Using a photoelectric sensor to turn on hallway lights'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-rDHUVZ5lJ_A/Tf9FcmHEElI/AAAAAAAAApg/IhC3qbiZ4Dc/s72-c/pz_51l_200px.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-793461351690347659</id><published>2011-06-17T01:20:00.001+08:00</published><updated>2011-06-17T10:32:01.330+08:00</updated><title type='text'>First VIWC test</title><content type='html'>Been waiting for the heavens to start shedding tears. It just did. Caught the tail end of a short pour. Quickly got into the car and parked it outdoors. Fortunately I had some 10min of drizzle. Turned the &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_928.html"&gt;VIWC&lt;/a&gt; on. Waited for the tiny raindrops to accumulate ... waited... waited ... and finally had the wiper sweep the windshield while the view was still relatively unobstructed. Stopwatch shows the time interval was 1min 18s. During the latter half of that test, I held down the button to reset the circuit and next interval was 1:43. &lt;br /&gt;&lt;br /&gt;Well with such long periods, this completely trashes the &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_10.html"&gt;potentiometer VIWC&lt;/a&gt; with its 32 or 64s max time interval. Of course we can always modify the firmware to use 7 or even 8 bits of the ADC output giving us 128 or 256 seconds max, but then adjusting the pot would be a most tricky thing. A multiturn pot might be required. But using that is going to be awkward if not a pain in the neck. In conjunction with changing the number of ADC bits (or not changing them), we could also &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_12.html"&gt;increase the increment/step&lt;/a&gt; from 1 to 2s or even more seconds.&lt;br /&gt;&lt;br /&gt;This is another distinct advantage of the push-button, elapsed-time-recording VIWC. The time resolution can be programmed to be as coarse or fine as required/desired. In this case we've set it to 100ms. To alter that we need only change the timer1 tick. If we're decreasing the tick to say 10ms (but who needs that kind of resolution on a wiper?) we'd want to make ELAPSEDTIME a 24- or 32-bit integer to be able to measure the equivalent amount of time. &lt;br /&gt;&lt;br /&gt;The test evinced the superiority of this type of VIWC. The driver need not focus on adjusting the time interval--he need only press the button when the windshield gets too cluttered. The only problem I had was that the dry windshield initially had a thin layer of dust on it which only became apparent after the first wiper wipe smeared the dust-turned-mud across the glass. I didn't but I could've turned off the VIWC and then pulled the wiper stalk to activate the front mist + wiper, and then switch back to VIWC. Or alternately, without turning off the VIWC, I could've pulled the stalk--and since the Innova's wiper module is deactivated--it would've sprayed water without the wiper action. I could then press button PB such that it sets the time interval to its minimum of 2sec, and then after the windshield had been cleaned, I could hold down PB to go into reset mode and set the proper wipe time interval. Sounds complicated. So there's probably room for improvement in this area. &lt;br /&gt;&lt;br /&gt;The real test of this circuit will come when I actually get to drive in the rain.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-793461351690347659?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/793461351690347659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/first-viwc-test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/793461351690347659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/793461351690347659'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/first-viwc-test.html' title='First VIWC test'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-6950256285179063831</id><published>2011-06-14T01:39:00.002+08:00</published><updated>2011-06-16T18:52:37.769+08:00</updated><title type='text'>Variable intermittent wiper control - Part 5</title><content type='html'>&lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_12.html"&gt;Variable intermittent wiper control - Part 4&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As  broached in Part 4, I'm gaga over a new way (at least to me) of  implementing a VIWC. It involves letting the circuit record time elapsed  between wipes and button presses to set the time interval for  windhshield wipes. As stated this can be accomplished using just one  momentary contact switch as user input. &lt;br /&gt;&lt;br /&gt;I've already  modified the "old" breadboarded potentiometer-based VIWC and  bench-tested it to be working. Circuitwise, the only difference between  the new more user-friendly VIWC and the former is the replacement of the  pot with a momentary contact switch, the addition of a Sonalert buzzer  to provide user feedback, and the use of a different MCU--the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en027148"&gt;PIC12F615&lt;/a&gt;. The interface with the Innova's wiper module remains unchanged. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-fmklQjTZOZY/TfPBRgrvdBI/AAAAAAAAAnQ/60GbCjJZnN8/s1600/innova-viwc-12F615-schem.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="239" src="http://3.bp.blogspot.com/-fmklQjTZOZY/TfPBRgrvdBI/AAAAAAAAAnQ/60GbCjJZnN8/s400/innova-viwc-12F615-schem.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I've  dumped the 10F222 and chosen a midrange PIC since I'd like to have  interrupts to ease my code-writing job and so as to have access to more  timers. I always want more timers! &lt;br /&gt;&lt;br /&gt;There's really  nothing much else to say about the electronics so let's talk about what  this improved VIWC does and discuss the firmware. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What the VIWC Does:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Because  it's the bedrock on which the intermittent control rests let's first  define and discuss the two modes the wiper circuit can be in:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Reset  mode -- Entered into by pressing button PB for at least half a second.  Upon entering reset mode a short audible alert will sound. In this mode  circuit simply records time elapsed since the last wipe. No wipe will be  performed until user presses PB again (momentarily or otherwise).&lt;/li&gt;&lt;li&gt;Wipe mode -- Entered into by pressing PB for less than half a  second. Upon pressing, a single windshield wipe will be performed and  the elapsed time since the last wipe becomes the time interval for all  succeeding wipes. The circuit will now automatically sweep the  windshield using the recorded time interval until that time PB is  pressed again. &lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Upon power-up a wipe is performed and circuit is put  in reset mode. An audible alert (40ms beep followed by 40ms silence  followed by a 40ms beep) is issued to inform the driver of this status.  No further wipes will be performed until the user presses PB (for less  than 0.5s to enter wipe mode or greater than 0.5s to enter reset mode). &lt;br /&gt;&lt;br /&gt;When  the user presses PB for &amp;lt;0.5s the circuit enters wipe mode. A  wipe is immediately performed. The time interval between the last and  current wipe is recorded and used as the interval for all succeeding  wipes which are automatically performed until that time PB is pressed  again. &lt;br /&gt;&lt;br /&gt;If the next button press is &amp;lt;0.5s then  as before a wipe is immediately performed and the time interval between  the last and current wipe is recorded and used as the interval for all  succeeding wipes. This implies that momentary button presses  (&amp;lt;0.5s) can be used to shorten wipe time intervals but not  lengthen them. To do that reset mode is necessary to  "teach" the  circuit the desired longer time interval.&lt;br /&gt;&lt;br /&gt;If the user  holds PB down for at least 0.5s the circuit enters reset mode. An  audible alert sounds and the circuit will keep recording the time  elapsed since the last wipe while waiting for a button press. No further  wipes will be performed until PB is pressed.&lt;br /&gt;&lt;br /&gt;The following example should make the operation crystal clear.&lt;br /&gt;&lt;br /&gt;It's  drizzling lightly so the driver switches on the VIWC. Audible alert  sounds to inform the driver that reset mode is active. Wiper sweeps the  windshield once. After 13 seconds the view through the windshield  becomes unduly hindered by the amount of raindrops. The driver presses  button PB for a fraction of second. A wipe action is immediately  performed. Now every 13 seconds thereafter the windshield is wiped. &lt;br /&gt;&lt;br /&gt;Some  time later the drizzle weakens, requiring wipe interval to be  increased. So the user holds the button down for over half a second. A  wipe is performed immediately upon pressing (&lt;i&gt;wipe&lt;/i&gt; mode is  entered). Half a second after button is pressed the audible alert sounds  signalling that reset mode is activated. Some 28 seconds later the  driver needs the windshield swept clean. So s/he presses PB momentarily.  A wipe is performed and the wipe time interval is set to 28 seconds.  The circuit then automatically wipes the windshield every 28 seconds  thereafter. &lt;br /&gt;&lt;br /&gt;After several minutes the drizzle begins  to intensify. After 17 seconds from the last wipe user pushes PB  momentarily to clean the windshield. Wiper switches on and time interval  is now set to 17 seconds. A couple more minutes pass and the rain  intensifies further. 5 seconds after the last wipe the user needs the  windshield cleared and so presses button momentarily. Wiper immediately  switches on. Circuit will now use a time interval of 5 seconds for  succeeding wipes.  &lt;br /&gt;&lt;br /&gt;In this example the amount of time  is explicitly stated only so that circuit operation can be succinctly  explained and clearly understood. Needless to say the driver does not  count or keep track of the number of seconds from the last wipe before  he presses the button.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Firmware Details&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;All  three timers of the 12F615 are used. Nominal timer tick periods given  their respective initialized parameters are as follows:&lt;br /&gt;&lt;br /&gt;timer0 = 8ms&lt;br /&gt;timer1 = 100ms&lt;br /&gt;timer2 = 50ms&lt;br /&gt;&lt;br /&gt;Timer0 tick determines when program execution in &lt;code&gt;main()&lt;/code&gt; occurs. Tick of 8ms was chosen for switch debouncing purposes. This is sufficient time to finish functions  &lt;code&gt;DebounceSwitch()&lt;/code&gt; and &lt;code&gt;ModeCheck()&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Timer1 is always on and used to measure elapsed time between wipes. Timer1 interrupt is always enabled.&lt;br /&gt;&lt;br /&gt;Timer2  is turned on when a wipe is performed and turned off when wipe is  compeleted. It is dedicated to determining how long the wiper motor will  be on. Timer2 interrupt is always enabled. &lt;br /&gt;&lt;br /&gt;Measurement  of wipe time interval: Timer 1 is used to measure wipe time interval.  Timer1's 16-bit register and prescaler as well as the 16-bit integer (&lt;code&gt;ELAPSEDTIME&lt;/code&gt;)  used to record elapsed time are all reset zero whenever wiper is turned  on. Therefore, elapsed time (and therefore wipe time interval) is  measured from the instant wiper is turned on to the time it is turned on  again. The user can set the time interval by pressing the button at two  different points in time. However, if measured time between button  presses is less than a minimum amount (2 seconds), then firmware will  set time interval = 2 seconds&lt;br /&gt;&lt;br /&gt;Button is debounced by taking readings every timer0 tick. The instantaneous value is shifted into the LSB of 8-bit register &lt;code&gt;PB.val&lt;/code&gt;. The oldest reading is discarded. Switch is deemed not bouncing if the last 8 readings (8 bits) are high or low (&lt;code&gt;PB.val&lt;/code&gt;  = 0xFF or 0x00). The voltage level of the switch is deemed to be high  if all 8 are high and low if all 8 bits are low. When the voltage level  changes--high to low or low to high--this is taken as a falling or  rising edge. &lt;br /&gt;&lt;br /&gt;The reset mode audible alert takes around  120ms to complete and thus temporarily breaks the neat timer0 tick  cadence. During this time PB is not read and debounced. &lt;br /&gt;&lt;br /&gt;If  PB is pressed within 1.5s from the moment wiper was switched on then no  wipe action occurs and timer1 and elapsed time counter are &lt;i&gt;not&lt;/i&gt; reset. In other words, any and all falling edges detected within that 1.5s period (constant &lt;code&gt;deadtime&lt;/code&gt;) are ignored. However, the button hold down time is still monitored and if it exceeds &lt;code&gt;resetmodetime&lt;/code&gt; then reset mode is entered.&lt;br /&gt;&lt;br /&gt;The various times related to the wiper being turned on are:&lt;br /&gt;Wiper cycle time (measured with stalk switched to INT) = ~1.3s&lt;br /&gt;Wiper on time (determined by constant &lt;code&gt;wipertrigtime&lt;/code&gt;) = 1s&lt;br /&gt;PB press dead time (determined by constant &lt;code&gt;deadtime&lt;/code&gt;) = 1.5s&lt;br /&gt;Mininum wipe time interval (determined by constant &lt;code&gt;mininterval&lt;/code&gt;) = 2s&lt;br /&gt;&lt;br /&gt;The relationship between them is &lt;code&gt;wipertrigtime&lt;/code&gt; &amp;lt; wiper cycle time &amp;lt; &lt;code&gt;deadtime&lt;/code&gt; &amp;lt;= &lt;code&gt;mininterval&lt;/code&gt;. The following illustration says it all. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Hi8YtqOiB1c/TfWz5uaUJyI/AAAAAAAAAnY/wA_CWZUBu7w/s1600/innova-time-constants.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="154" src="http://2.bp.blogspot.com/-Hi8YtqOiB1c/TfWz5uaUJyI/AAAAAAAAAnY/wA_CWZUBu7w/s400/innova-time-constants.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;code&gt;wipertrigtime &lt;/code&gt;must of course be &amp;lt; wiper cycle time so that the wiper parks properly. &lt;code&gt;deadtime&lt;/code&gt; has to be &amp;gt; wiper cycle time in order that the wiper isn't retriggered before it has parked. And naturally &lt;code&gt;mininterval&lt;/code&gt; should be at least equal to &lt;code&gt;deadtime&lt;/code&gt;. It doesn't make sense to have it less than &lt;code&gt;deadtime&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Holding  down PB for an extended period will merely keep the circuit in reset  mode. And the audible alert will keep repeating. It does not reset the  elapsed time counter. That only happens if wipe action occurs. And with  respect to PB that happens only during a falling edge detect (unless  that falling edge occurs within the PB dead time). So the user always  knows when elapsed time measurement begins and ends just by noting when  the wiper blades move.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt;&lt;br /&gt;Variable Intermittent Wiper Circuit that Learns from User Input&lt;br /&gt;for the Toyota Innova&lt;br /&gt;&lt;br /&gt;June 2011&lt;br /&gt;Edwardson Tan&lt;br /&gt;&lt;br /&gt;processor = PIC12F615&lt;br /&gt;compiler = mikroC Pro v4.60&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define  buzzer              GPIO.f4             // sonalert buzzer&lt;br /&gt;#define  wiper               GPIO.f5             // 2N7000 switches relay which switches wiper low speed&lt;br /&gt;#define  pb                  GPIO.f2             // momentary contact switch&lt;br /&gt;&lt;br /&gt;#define  tris_pb             TRISIO.f2           // used to set pb pin as input&lt;br /&gt;#define  wpu_pb              WPU.f2              // used to enable weak pull up for pb&lt;br /&gt;&lt;br /&gt;#define  t0_init_val         256 - 250           // value loaded into TMR0 every time it overflows&lt;br /&gt;#define  t0tick              8                   // timer0 tick time in milliseconds = [(256 - t0_init_val) * prescale / 1MHz] * 1000ms&lt;br /&gt;&lt;br /&gt;#define  t1h_init_val        256 - 195           // value loaded into TMR1H every time it rolls over to zero for timer1 to count 0.25sec at a prescale = 1:8 and clock of 4MHz&lt;br /&gt;#define  t1tick              100                 // timer1 tick time in milliseconds = [(256 - t1_init_val) * 256 * prescale / 1MHz] * 1000ms&lt;br /&gt;&lt;br /&gt;#define  t2tick              50                  // timer2 tick time in milliseconds = [PR2 * prescale * postscale / 1MHz] * 1000ms&lt;br /&gt;&lt;br /&gt;#define  resetmodetime       500/t0tick          // mininum amount of time pb is held down before reset mode is entered. time in terms of timer0 ticks&lt;br /&gt;#define  wipertrigtime       1000/t2tick         // amount of time wiper is turned on. time is in terms of timer2 ticks&lt;br /&gt;#define  deadtime            1500/t1tick         // mininum elapsed time before a button press will trigger a wipe. time is in terms of timer1 ticks&lt;br /&gt;#define  mininterval         2000/t1tick         // mininum time interval between wipes. time is in terms of timer1 ticks&lt;br /&gt;&lt;br /&gt;#define  mode                FEN.f0              // 0 = wipe mode,  1 = reset mode&lt;br /&gt;#define  wipe                0&lt;br /&gt;#define  reset               1&lt;br /&gt;&lt;br /&gt;#define  rising              1                   // rising edge detected. used by PB.edge&lt;br /&gt;#define  released            1                   // falling edge detected. used by PB.edge&lt;br /&gt;#define  falling             2                   // falling edge detected. used by PB.edge&lt;br /&gt;#define  pressed             2                   // rising edge detected. used by PB.edge&lt;br /&gt;#define  none                0                   // no edge. used by PB.edge&lt;br /&gt;&lt;br /&gt;#define  int8                unsigned char&lt;br /&gt;#define  int16               unsigned int&lt;br /&gt;#define  int32               unsigned long&lt;br /&gt;&lt;br /&gt;#define  on                  1&lt;br /&gt;#define  off                 0&lt;br /&gt;&lt;br /&gt;#define  yes                 1&lt;br /&gt;#define  no                  0&lt;br /&gt;&lt;br /&gt;#define  input               1         // for TRISx&lt;br /&gt;#define  output              0         // for TRISx&lt;br /&gt;&lt;br /&gt;#define  analog              1         // for ANSELx&lt;br /&gt;#define  digital             0         // for ANSELx&lt;br /&gt;&lt;br /&gt;#define  hi                  1         // switch level high&lt;br /&gt;#define  lo                  0         // switch level low&lt;br /&gt;&lt;br /&gt;#define  all_bits_hi         0xFF      // for setting an 8-bit register to 255&lt;br /&gt;#define  all_bits_lo         0         // for setting an 8-bit register to 0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;//       Global Variables&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;struct&lt;br /&gt;{&lt;br /&gt;  val : 8;                             // last eight readings push button. if 0x0 then level is lo, if it is = all_bits_hi then level is hi, any other value means switch is bouncing&lt;br /&gt;  level : 1;                           // level of push button when not bouncing (hi = 1, lo = 0)&lt;br /&gt;  edge : 2;                            // 0 = no edge detect, 1 = rising edge, 2 = falling edge; 3 = Not Used / Undefined for now&lt;br /&gt;}&lt;br /&gt;PB;&lt;br /&gt;&lt;br /&gt;int8    PBHELDDOWN;                    // number of timer0 overflows pb has been held down (ie. how long PB.level has been low)&lt;br /&gt;int16   ELAPSEDTIME;                   // elapsed time since the last wipe, in terms of timer1 ticks&lt;br /&gt;int16   WIPEINTERVAL;                  // user selected time interval between wipes, in terms of timer1 ticks&lt;br /&gt;int8    T2COUNTER = 0;                 // stores number of timer2 interrupts that have occured. for use with wiper on time&lt;br /&gt;int8    FEN = 0;                       // Flag and ENable and status bits register&lt;/pre&gt;&lt;pre&gt;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;// ==================================================================================================================&lt;br /&gt;//       Functions&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;// 1 centisecond = 10 milliseconds = 0.01sec&lt;br /&gt;void DelayCentisec(int8 csec)&lt;br /&gt;{&lt;br /&gt;  int8 i;&lt;br /&gt;  for (i=1; i&amp;lt;=csec; i++)&lt;br /&gt;  {&lt;br /&gt;    Delay_ms(10);&lt;br /&gt;    asm{clrwdt}                        // WDT timeout = 18ms. Therefore it must be cleared here&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// note: this routine takes over a hundred milliseconds to execute, obviously! it will not finish within the timer0 tick of 8ms&lt;br /&gt;void BeepReset()&lt;br /&gt;{&lt;br /&gt;  buzzer = on;&lt;br /&gt;  DelayCentisec(4);&lt;br /&gt;  buzzer = off;&lt;br /&gt;  DelayCentisec(4);&lt;br /&gt;  buzzer = on;&lt;br /&gt;  DelayCentisec(4);&lt;br /&gt;  buzzer = off;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// wiper is turned on&lt;br /&gt;// timer2 is turned on&lt;br /&gt;// timer1 registers and prescaler and 16-bit variable that records elapsed time since last wipe are all reset to zero&lt;br /&gt;// note: wiper will be turned off in the ISR&lt;br /&gt;void Wipe()&lt;br /&gt;{&lt;br /&gt;  wiper = on;&lt;br /&gt;  T2CON.TMR2ON = on;&lt;br /&gt;  TMR1L = 0;&lt;br /&gt;  TMR1H = t1h_init_val;&lt;br /&gt;  ELAPSEDTIME = 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void InitRegisters()&lt;br /&gt;{&lt;br /&gt;  // I/O pins&lt;br /&gt;  // all are output except for pb, weak pull enabled for pb, all I/Os are digital, clear output pins&lt;br /&gt;  GPIO = 0;&lt;br /&gt;  ANSEL = digital;&lt;br /&gt;  TRISIO = output;&lt;br /&gt;  tris_pb = input;&lt;br /&gt;  wpu_pb = on;&lt;br /&gt;&lt;br /&gt;  // timer0 registers&lt;br /&gt;  // given 4MHz clock, prescale = 1:32, TMR0 init val = 6&lt;br /&gt;  // timer0 tick = (256 - 6)(32)(1us) = 8.00ms&lt;br /&gt;  // TMR0 doesn't need to be initialized to any value since the first wipe action will take one second (firmware waits until this is wipe is over) and by then T0IF will be set whatever TMR0 initial value was&lt;br /&gt;  OPTION_REG = 0b100;                  // global weak pull ups on, prescaler assigned to timer0, prescale = 1:32&lt;br /&gt;&lt;br /&gt;  // timer1 registers&lt;br /&gt;  // given 4MHz clock, prescale = 1:2, TMR1H init val = 61&lt;br /&gt;  // timer1 tick = (256 - 61)(256)(2)(1us) = 99.84ms&lt;br /&gt;  // upon power up timer1 runs continuously; it is never stopped, but TMR1L and TMR1H are reinitialized upon button press&lt;br /&gt;  T1CON = 0b010001;                    // prescale = 1:2, timer1 source = internal clock, timer1 on&lt;br /&gt;  TMR1L = 0;&lt;br /&gt;  PIE1.TMR1IE = on;&lt;br /&gt;&lt;br /&gt;  // timer2 registers&lt;br /&gt;  // given 4MHz clock, prescale = 1:16, postscale = 1:16, PR2 = 195&lt;br /&gt;  // timer2 tick = (195)(16)(16)(1us) = 49.92ms&lt;br /&gt;  // timer2 dedicated to&lt;br /&gt;  T2CON = 0b1111011;                   // postscale = 1:16, timer2 off, prescale = 1:16&lt;br /&gt;  PR2 = 195;                           // when timer2 running TMR2 will increment until it matches PR2 and then TMR2 will be reset&lt;br /&gt;  TMR2 = 0;&lt;br /&gt;  PIE1.TMR2IE = on;&lt;br /&gt;&lt;br /&gt;  // initialize to no-button-press condition&lt;br /&gt;  PB.val = all_bits_hi;&lt;br /&gt;  PB.level = hi;&lt;br /&gt;  PB.edge = none;&lt;br /&gt;  &lt;br /&gt;  mode = reset;                        // circuit starts up in reset mode, i.e., wiper will sweep windshield once upon power up and then wait for user to trigger wiper to lock in wipe time interval&lt;br /&gt;  WIPEINTERVAL = mininterval;&lt;br /&gt;&lt;br /&gt;  INTCON.GIE = on;&lt;br /&gt;  INTCON.PEIE = on;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// The following debounce and edge and level detect routine is performed every timer0 tick&lt;br /&gt;void DebounceSwitch()&lt;br /&gt;{&lt;br /&gt;  // shift all bits to the left&lt;br /&gt;  // if switch reading is hi then PB.val bit0 gets set&lt;br /&gt;  PB.val &amp;lt;&amp;lt;= 1;&lt;br /&gt;  if (pb)&lt;br /&gt;    ++PB.val;&lt;br /&gt;&lt;br /&gt;  // if pb has been pressed then start counting how long it is kept pressed&lt;br /&gt;  // a key is said to be pressed when level is lo&lt;br /&gt;  // if level is high then reset counter&lt;br /&gt;  if (!PB.level)&lt;br /&gt;    ++PBHELDDOWN;&lt;br /&gt;  else&lt;br /&gt;    PBHELDDOWN = 0;&lt;br /&gt;&lt;br /&gt;  // assume no edge detected&lt;br /&gt;  PB.edge = none;&lt;br /&gt;  &lt;br /&gt;  // if level is lo and all bits of PB.val are now hi then&lt;br /&gt;  // a rising edge has been detected&lt;br /&gt;  // switch is considered just released when rising edge is detected&lt;br /&gt;  // switch level is now hi&lt;br /&gt;  if ((!PB.level) &amp;amp;&amp;amp; (PB.val == all_bits_hi))&lt;br /&gt;  {&lt;br /&gt;    PB.level = hi;&lt;br /&gt;    PB.edge = rising;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // if level is hi and all bits of PB.val are now low then&lt;br /&gt;  // a falling edge has been detected&lt;br /&gt;  // switch is considered just pressed when falling edge is detected&lt;br /&gt;  // switch level is now lo&lt;br /&gt;  if ((PB.level) &amp;amp;&amp;amp; (PB.val == 0))&lt;br /&gt;  {&lt;br /&gt;    PB.level = lo;&lt;br /&gt;    PB.edge = falling;&lt;br /&gt;  }&lt;br /&gt;} // void DebounceSwitch()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// This function will check if pb has been pressed&lt;br /&gt;// If pressed then wipe action will be issued and wipe mode is entered&lt;br /&gt;// However, button press--falling edge dected--is ignored if it occurs less than deadtime from the last wipe&lt;br /&gt;// if button is held down &amp;gt; for a time greater than resetmodetime then reset mode is entered.&lt;br /&gt;// note: ELAPSEDTIME is reset to zero in the function Wipe()&lt;br /&gt;void ModeCheck()&lt;br /&gt;{&lt;br /&gt;  if (PB.edge == falling &amp;amp;&amp;amp; ELAPSEDTIME &amp;gt;= deadtime)&lt;br /&gt;  {&lt;br /&gt;    WIPEINTERVAL = ELAPSEDTIME;&lt;br /&gt;    if (WIPEINTERVAL &amp;lt; mininterval)&lt;br /&gt;      WIPEINTERVAL = mininterval;&lt;br /&gt;    mode = wipe;&lt;br /&gt;    Wipe();&lt;br /&gt;  }&lt;br /&gt;  else if (PBHELDDOWN &amp;gt;= resetmodetime)&lt;br /&gt;  {&lt;br /&gt;    mode = reset;&lt;br /&gt;    PBHELDDOWN = 0;&lt;br /&gt;    BeepReset();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void interrupt()&lt;br /&gt;{&lt;br /&gt;  if (PIR1.TMR1IF)&lt;br /&gt;  {&lt;br /&gt;    TMR1H = t1h_init_val;&lt;br /&gt;    PIR1.TMR1IF = off;&lt;br /&gt;    ++ELAPSEDTIME;&lt;br /&gt;    if ((mode == wipe) &amp;amp;&amp;amp; (ELAPSEDTIME &amp;gt;= WIPEINTERVAL))    // if time interval has already been entered by user, then wipe action will automatically be performed after every time interval&lt;br /&gt;      Wipe();&lt;br /&gt;  } // if (PIR1.TMR1IF)&lt;br /&gt;  &lt;br /&gt;  if (PIR1.TMR2IF)&lt;br /&gt;  {&lt;br /&gt;    PIR1.TMR2IF = 0;&lt;br /&gt;    if (++T2COUNTER &amp;gt;= wipertrigtime)&lt;br /&gt;    {&lt;br /&gt;      wiper = off;&lt;br /&gt;      T2CON.TMR2ON = off;&lt;br /&gt;      T2COUNTER = 0;&lt;br /&gt;      TMR2 = 0;&lt;br /&gt;    }&lt;br /&gt;  } // if (PIR1.TMR2IF)&lt;br /&gt;} // void interrupt()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;//       main&lt;br /&gt;// ==================================================================================================================&lt;br /&gt;&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  InitRegisters();&lt;br /&gt;  Wipe();&lt;br /&gt;  BeepReset();&lt;br /&gt;  // while(wiper);              // wait till wipe has finished before proceeding, so that any pb press will have no effect&lt;br /&gt;  &lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    if (INTCON.T0IF)&lt;br /&gt;    {&lt;br /&gt;      asm{clrwdt}&lt;br /&gt;      INTCON.T0IF = off;&lt;br /&gt;      TMR0 = t0_init_val;&lt;br /&gt;      DebounceSwitch();&lt;br /&gt;      ModeCheck();&lt;br /&gt;    } // if (INTCON.T0IF)&lt;br /&gt;  } // while(1)&lt;br /&gt;} // void main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Testing the VIWC&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To see if the circuit and firmware are really working as expected I hooked up the &lt;a href="http://www.saleae.com/logic/"&gt;Saleae&lt;/a&gt;  logic analyzer to the PIC12F615's GP2 pin (PB) and GP5 (Q1 gate). In  the text Q1 was driving only an LED not REL2. A really small tactile  switch was used for PB. Circuit was powered via &lt;a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en023805&amp;amp;redirects=pickit2"&gt;PICkit2&lt;/a&gt; set to 5.0V. In all of the screenshots below channel 6 is hooked up to GP5 and channel 7 to GP2.&lt;br /&gt;&lt;br /&gt;I  wish I could've set the sampling rate to 1MHz and higher but my  decade-old computer only has USB1.0 which limits the Saleae to 500kHz.  Not infrequently it can't even maintain that and I've experienced the  rate going down to 200kHz. &lt;br /&gt;&lt;br /&gt;In the first screen capture  below the green vertical cursors 1 and 2 have been positioned at the  falling edge of GP2 and rising edge of GP5. You can see that |T1 - T2| =  62.6ms. This delay between PB press and wiper switch on is as expected  due to the switch debouncing routine which requires all 8 bits of &lt;code&gt;PB.val&lt;/code&gt;  to be either high or low before a level change is said to have  occurred. Those eight bits take eight timer0 ticks. At 8ms, we would  expect a nominal 64ms from the actual falling edge of GP2 to the moment  wiper is switched on. &lt;br /&gt;&lt;br /&gt;Take a look at the amount of  time GP5 is high. The pulse width is almost exactly 1 second as we have  specified. Note the second falling edge of GP2--PB was pressed again  momentarily--causing a wipe action to occur and setting the wipe  interval = 4 seconds (read the item labeled "Period")&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ql4AxbWk2BQ/TfYhA_8BqCI/AAAAAAAAAng/TzGoljTcNgk/s1600/saleae%2B2011jun13%2B01a.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-ql4AxbWk2BQ/TfYhA_8BqCI/AAAAAAAAAng/TzGoljTcNgk/s400/saleae%2B2011jun13%2B01a.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Zooming in and measuring how long PB was pressed we see that the pulse width is almost 120ms.&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-fFvedyJHZ-Y/TfYhBF41CMI/AAAAAAAAAno/cLRFgu-f3zE/s1600/saleae%2B2011jun13%2B01b.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://2.bp.blogspot.com/-fFvedyJHZ-Y/TfYhBF41CMI/AAAAAAAAAno/cLRFgu-f3zE/s400/saleae%2B2011jun13%2B01b.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The  shot below shows the wipe interval of succeeding wipes is still  4seconds (3.98s in the instance below). Keep in mind that timer1 tick =  100ms so that's the tolerance value.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-mfM3MFKBbr8/TfYhBpiVhgI/AAAAAAAAAnw/jKGKKZOwc8o/s1600/saleae%2B2011jun13%2B01c.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-mfM3MFKBbr8/TfYhBpiVhgI/AAAAAAAAAnw/jKGKKZOwc8o/s400/saleae%2B2011jun13%2B01c.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I intentionally waited for the LED to light and then pressed PB rapidly. Remember we specified &lt;code&gt;deadtime&lt;/code&gt;  = 1.5s so a falling edge on or after an elapsed time of 1.5sec after  GP5 goes high should be recognized. Well apparently this did not happen  as the shot below shows. Wiper was turned on only after some 1.92s. Is  the firmware buggy? &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-656drglF-_w/TfYhCLyCLDI/AAAAAAAAAn4/nbu-dpClQsc/s1600/saleae%2B2011jun13%2B01d.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-656drglF-_w/TfYhCLyCLDI/AAAAAAAAAn4/nbu-dpClQsc/s400/saleae%2B2011jun13%2B01d.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;After  closer inspection it turns out that I was pressing PB at such a rate  that some presses (level low) and nonpresses (level high) were shorter  than the required debouncing period of 64ms. Take a look at T1-T2 where  cursor 2 is at a falling edge of PB. It's 1.7s. Wiper ought to have been  turned on some 64ms after this edge. But then look at the pulse width  (rising edge to falling edge). The pulse was high for only 49ms. Because  not all bits were high the firmware took this as indicative of switch  contacts that are bouncing and thus no valid level change (edge detect)  was recognized. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/--KkxeHoKNZ0/TfYhCcwM56I/AAAAAAAAAoA/i_B0DTfXGh4/s1600/saleae%2B2011jun13%2B01e.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://2.bp.blogspot.com/--KkxeHoKNZ0/TfYhCcwM56I/AAAAAAAAAoA/i_B0DTfXGh4/s400/saleae%2B2011jun13%2B01e.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The next button release and button press, on the other hand, met the minimum debouncing requirements: high  time was 87ms while low time = 203ms - 87ms = 116ms. Hence wiper was  switched on after the valid falling edge was recognized.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-0RlhSZbAlk0/TfYhYz2DE4I/AAAAAAAAAoY/JSTnZIl3m-Q/s1600/saleae%2B2011jun13%2B01f.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-0RlhSZbAlk0/TfYhYz2DE4I/AAAAAAAAAoY/JSTnZIl3m-Q/s400/saleae%2B2011jun13%2B01f.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-1a8xZ8Dj1V0/TfYhYv7af6I/AAAAAAAAAoQ/k7iD8djAhmQ/s1600/saleae%2B2011jun13%2B01g.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-1a8xZ8Dj1V0/TfYhYv7af6I/AAAAAAAAAoQ/k7iD8djAhmQ/s400/saleae%2B2011jun13%2B01g.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Recall  that the measured wipe interval was 1.92s. This is less than the  minimum of 2s we specified. Thus for the succeeding wipes, firmware set  the wipe interval to 2s, with actual period measured to be 1.99s.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-1uMTOqdSMb4/TfYhYhMCBtI/AAAAAAAAAoI/4jq7ojq0IW4/s1600/saleae%2B2011jun13%2B01h.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-1uMTOqdSMb4/TfYhYhMCBtI/AAAAAAAAAoI/4jq7ojq0IW4/s400/saleae%2B2011jun13%2B01h.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The  next screenshots are from a different set of data capture. In this  first image time between GP2 falling edge and GP5 rising edge is 60ms.  The wipe interval is a mere 1.52ms--this violates the minimum and so we  should see it changed to 2s in the next wipe (if no button presses occur  within that interval).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-5N-FxI3yjQk/TfYw5tue-EI/AAAAAAAAAog/1HZe-Ejlyyg/s1600/saleae%2B2011jun13%2B02a.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-5N-FxI3yjQk/TfYw5tue-EI/AAAAAAAAAog/1HZe-Ejlyyg/s400/saleae%2B2011jun13%2B02a.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I  moved the cursors to measure the the elapsed time from the moment wiper  was turned on to the moment when the falling edge which triggers the  wiper occurs. T1-T2 = 1.46s. Doesn't this violate the 1.5s deadtime  requirement? No it doesn't. Remember that the debouncing routine needs  64ms to "see" an actual falling edge. So the nominal dead time between  wiper turn on and &lt;i&gt;actual&lt;/i&gt; (in contrast to firmware determined) falling edge = 1.5s - 64ms = ~1.44s&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hb82jxsyvWw/TfYw52NIa7I/AAAAAAAAAoo/xXV-M4CGz6Y/s1600/saleae%2B2011jun13%2B02b.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-hb82jxsyvWw/TfYw52NIa7I/AAAAAAAAAoo/xXV-M4CGz6Y/s400/saleae%2B2011jun13%2B02b.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;As  expected in the very next wipe the time interval has been adjusted to  conform to the minimum of 2s. Actual measured period = 1.99s. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/--EqIYW2D6uA/TfYw6HSB_iI/AAAAAAAAAow/yvmejZZDH7w/s1600/saleae%2B2011jun13%2B02c.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://2.bp.blogspot.com/--EqIYW2D6uA/TfYw6HSB_iI/AAAAAAAAAow/yvmejZZDH7w/s400/saleae%2B2011jun13%2B02c.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Here  I held down PB while GP5 was still high (LED still on). Pulse width  (time PB was held down) = 1.05s. Having pressed PB during the dead time,  no wipe occurred. But because PB was pressed for &amp;gt;0.5s, reset  mode became active--as can be seen no wipe occurred after 2s (the wipe  time interval before the reset). Wiper turns on again only after PB was  pressed again.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-fsbgeP2aigs/TfYxJJBWlMI/AAAAAAAAApI/njdVfdRxCpY/s1600/saleae%2B2011jun13%2B02d.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-fsbgeP2aigs/TfYxJJBWlMI/AAAAAAAAApI/njdVfdRxCpY/s400/saleae%2B2011jun13%2B02d.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;These  last two shots show that elapsed time / wipe interval is measured from  the moment wiper is switched on (GP5 rising edge) and not when PB is  pressed. As you can see the period is 4.18s in both cases even if PB was  pressed (to enter reset mode) almost a second after wiper was turned  on.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-EK2hrY0YR54/TfYxIv5wX5I/AAAAAAAAApA/yPn7pk5CBI8/s1600/saleae%2B2011jun13%2B02e.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://3.bp.blogspot.com/-EK2hrY0YR54/TfYxIv5wX5I/AAAAAAAAApA/yPn7pk5CBI8/s400/saleae%2B2011jun13%2B02e.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-SstpCGADPl0/TfYxIYAfJQI/AAAAAAAAAo4/fMWLe3GonhI/s1600/saleae%2B2011jun13%2B02f.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://1.bp.blogspot.com/-SstpCGADPl0/TfYxIYAfJQI/AAAAAAAAAo4/fMWLe3GonhI/s400/saleae%2B2011jun13%2B02f.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So  there you have it. A more or less full documentation of what for me is  an exciting implementation of a VIWC. I'm not rushing to fabricate a PCB  for it though. I feel there's a need to have an extended pilot test to  get a feel for how well it works and to see if there's anything that  needs to modified, tweaked, revised. So I'm stashing the prototype and  relays and the whole tangle of wires under the dashboard and installing  temporary switches on the lower part of the dash for now. And obviously  I'll be doing daily rain dances for the next two weeks.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;[June 14 edit: I commented out the line "&lt;code&gt;while(wiper);&lt;/code&gt;" in &lt;code&gt;main()&lt;/code&gt;. That line is unnecessary]&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;[June 16 edit: Forgot to mention the maximum wipe time interval possible. Given timer1 tick = 0.1s and &lt;code&gt;ELAPSEDTIME&lt;/code&gt; being a 16--bit variable, the maximum time the variable can measure before overflowing back to zero = 65,535 * 0.1s = 6553.5s or ~109 minutes. I'm sure! that's sufficient for lightest of drizzles.]&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-6950256285179063831?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/6950256285179063831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_928.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6950256285179063831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6950256285179063831'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_928.html' title='Variable intermittent wiper control - Part 5'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-fmklQjTZOZY/TfPBRgrvdBI/AAAAAAAAAnQ/60GbCjJZnN8/s72-c/innova-viwc-12F615-schem.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4665256213008129287</id><published>2011-06-12T00:58:00.016+08:00</published><updated>2011-06-14T01:41:10.159+08:00</updated><title type='text'>Variable intermittent wiper control - Part 4</title><content type='html'>&lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_10.html"&gt;Variable intermittent wiper control - Part 3 &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It seems I have a couple more things to say about the circuit, matters that are rather important. So this entry will be about tying up loose ends, and then some. &lt;br /&gt;&lt;br /&gt;1. When the VIWC is powered, switching the stalk to any of the settings which turns on the front windshield wiper (INT, LO, HI, momentary wipe) will not perform any operation. This is because the wiper motor needs power via W-VDD and W-PP and the module's electronics needs +12V from W-VDD. The only setting that will work--partly at least--is when the spring-loaded stalk is pulled toward the driver. This is the front mist + wiper setting. When the stalk is pulled, the front mist motor will operate and spray water, but the wiper will be nonoperational.&lt;br /&gt;&lt;br /&gt;On the other hand, the rear wiper and rear mist are fully functional even with VIWC on. The rotary switch (knob) on the stalk that controls these functions operates normally. This is of course desirable. We'd want to be able to operate the rear wiper and washer regardless of whether the VIWC is on or off. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. If we want longer time intervals than the 32 seconds we can derive a 6-bit ADC value instead of a 5-bit number. That would give us a maximum of 64 seconds given our algorithm. &lt;br /&gt;&lt;br /&gt;We can also vary the step function, i.e., the increment. For instance we can convert the ADC value from 8 to 5 bits and then add 2:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;return ((ADRES &amp;gt;&amp;gt; 3) + 2); &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The range of time intervals will then be 2, 4 ,6 ... 64 seconds.&lt;br /&gt;&lt;br /&gt;For fractional values, say increments of 1.5 seconds we can do the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;TIME = (ADC())*20 + 10; &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;10 timer ticks = half a second, so the above code will add 0.5sec to the increment value. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3. I used the &lt;a href="http://www.saleae.com/logic/"&gt;Saleae Logic&lt;/a&gt; to measure the wipe time interval for a few potentiometer settings. Images show below 1, 16, and 32 second intervals. Look at the value for the item marked"Width" for the actual time intervals for the particular chip I used. Probe was connected to 10F222's GP2 pin.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-VkwyuNJKiVA/TfOwfcFncTI/AAAAAAAAAm4/9MlJKjD6ejM/s1600/saleae%2B1sec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://2.bp.blogspot.com/-VkwyuNJKiVA/TfOwfcFncTI/AAAAAAAAAm4/9MlJKjD6ejM/s400/saleae%2B1sec.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ZFn2J0pHLYw/TfOwfpp-G6I/AAAAAAAAAnA/fENVcRQrV6k/s1600/saleae%2B16sec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://2.bp.blogspot.com/-ZFn2J0pHLYw/TfOwfpp-G6I/AAAAAAAAAnA/fENVcRQrV6k/s400/saleae%2B16sec.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-jH-_DTT6uLk/TfOwgB-3EPI/AAAAAAAAAnI/2UhMxRMLJg8/s1600/saleae%2B32sec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://1.bp.blogspot.com/-jH-_DTT6uLk/TfOwgB-3EPI/AAAAAAAAAnI/2UhMxRMLJg8/s400/saleae%2B32sec.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;4. Just as I finished writing Part 3 I stumbled upon a &lt;a href="http://www.omegaowners.com/forum/YaBB.pl?num=1281183577/0#0"&gt;forum&lt;/a&gt; discussing a most creative and elegant type of variable intermittent wiper. The circuit learns how long the time interval between wipes by monitoring when the user flips a couple of switches.&lt;br /&gt;&lt;blockquote&gt;This wiper delay timer "learns" the wiper interval in the following way: It  starts drizzling, so you flick the wipers once. It keeps drizzling so,  when the screen is in need of another wipe, you flick it onto  intermittent. The wiper delay timer sets itself to the time interval  between your initial "flick" of the wipers and when you switched it to  intermittent, and carries on using that interval thereafter. &lt;/blockquote&gt;This is such a great and simple idea. And it's more user-friendly than continually tweaking a pot (while driving!) to get the timing right. I had one of those eye-rolling "Why didn't &lt;i&gt;I&lt;/i&gt; think of that" moment. &lt;br /&gt;&lt;br /&gt;After mulling over the switch requirements, the light bulb flashed, and I figured that for our version of this type of control one momentary contact button would suffice. By holding holding the button for about a second we can inform the MCU that we want to enter time interval record mode, i.e., the circuit will wait for us to press the button again (for just a fraction of second) to lock in the time interval we desire. Thereafter the circuit will use that time period to sweep the windshield. Thereafter should we press the button again for less than second the MCU will shorten the wipe interval time accordingly. Pressing the button for &amp;lt;1sec can't lengthen the time interval, only shorten it. Thus we need to enter record mode which can be done so at anytime by, as before, holding down the key for at least a second. &lt;br /&gt;&lt;br /&gt;Looks like the potentiometer-based VIWC has already been obsoleted and shelved even before I've designed a board for it. Which is just as well since I now don't have to expend so many hours on PCB design and fabrication and assembly of a circuit that I probably won't be installing anyway.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_928.html"&gt;Part 5&lt;/a&gt; I will have a full discussion of this new type of VIWC. Schematic and firmware will be provided and discussed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4665256213008129287?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4665256213008129287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_12.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4665256213008129287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4665256213008129287'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_12.html' title='Variable intermittent wiper control - Part 4'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-VkwyuNJKiVA/TfOwfcFncTI/AAAAAAAAAm4/9MlJKjD6ejM/s72-c/saleae%2B1sec.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-436460014898311465</id><published>2011-06-10T01:51:00.001+08:00</published><updated>2011-06-12T01:29:51.867+08:00</updated><title type='text'>Variable intermittent wiper control - Part 3</title><content type='html'>&lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_08.html"&gt;Variable intermittent wiper control - Part 2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Rather than use a 555 for the variable intermittent wiper circuit (VIWC) as we saw in &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control.html"&gt;Part 1&lt;/a&gt; I employed a microcontroller instead for the Innova. An MCU's timing accuracy is far better than the 555 and is far more powerful and flexible and requires less discrete components. &lt;br /&gt;&lt;br /&gt;I chose a baseline PIC with analog to digital converter (ADC) instead of a midrange PIC because I need only two pins and both Flash and RAM requirements are minimal. What I have on hand right now is the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en023672"&gt;PIC10F222&lt;/a&gt;, but the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en023673"&gt;10F220&lt;/a&gt; just might suffice depending on the optimization level available on the compiler--the Hi -Tech C freebie version I use throws up obscenely long codes and probably gobbles up RAM as well. &lt;br /&gt;&lt;br /&gt;Given the wiper module's electronics it is necessary to electrically disconnect it from the VIWC to prevent any conflicts and possible shorts. Through tests I've determined that the module's electronics is powered via W-VDD. So disconnecting the circuit from this net will prevent it from being powered up. However, we will be using W-PP and that goes right into the board. W-PP carries a +12V which could possibly wreak havoc on the circuitry particularly with W-VDD cut off. With the wiper stalk switch at OFF and INT, W-PP is connected to W-LO. This is actually fine but no harm in breaking this connection while the VIWC is powered up. Tests thus far show that W-LO isn't connected to the board or relay. So we don't have to sever this connection to the wiper module. We'll just need to tap into it to trigger--briefly turn on--the wiper motor. Here's a diagram of the wires leading to the connectors that we'd need to break. Actually only one connector needs to be modified.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ALWs6QC7T3E/Te97Y8fy7uI/AAAAAAAAAmI/LHWLfoRxXng/s1600/innova-new-wires.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://3.bp.blogspot.com/-ALWs6QC7T3E/Te97Y8fy7uI/AAAAAAAAAmI/LHWLfoRxXng/s320/innova-new-wires.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Take note of the new wire labels. Those are used in the following schematic. Note that all switches and relay contacts are shown in their normal position--with the circuit powered down. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-7qKMBBQiYfY/Te97YsNzHMI/AAAAAAAAAmA/mdYTXrvHn1w/s1600/innova-var-intermit-schemat.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://3.bp.blogspot.com/-7qKMBBQiYfY/Te97YsNzHMI/AAAAAAAAAmA/mdYTXrvHn1w/s320/innova-var-intermit-schemat.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In the initial design I opted to use a DPDT switch rather than the DPDT relay. I decided on the relay and SPST switch design because I would be able to use a smaller switch on the dashboard and would have to bring only two wires to it.&lt;br /&gt;&lt;br /&gt;REL1 is a &lt;a href="http://www.drillspot.com/products/439494/Telemecanique_RXM2AB2JD_Relay"&gt;Telemecanique RXM2AB2JD&lt;/a&gt;&amp;nbsp; 12-volt relay with 12A DPDT contacts and a green LED indicator lamp. REL2 is a 12-volt relay with 10A SPDT contacts.&lt;br /&gt;&lt;br /&gt;When S-POW is closed REL1 is energized. Power to wiper module is cut off while the VIWC is powered up. Simultaneously, WW-PP is disconnected from W-PP and connected instead to REL2's NC contact, thus making sure that when REL2 is de-energized the wiper motor will automatically bring the wiper blades back to their parking position. &lt;br /&gt;&lt;br /&gt;You will notice I have not used W-GND but connected the circuit directly to chassis ground. Tests show both are the same net even when ignition is off. I want to cut and cut into as few of the car's wires as possible. &lt;br /&gt;&lt;br /&gt;For the pot I used a cheap low quality one. Trying to get a particular time interval with it can become vexing. I'm still trying to find a better pot that has a much more even resistance across its entire range. Another option is to use a pot with a switch thus integrating S-POW with R1 in one package. &lt;br /&gt;&lt;br /&gt;A 78L05 100-mA voltage regulator provides +5V to the MCU. Filter cap is 100uF. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The microcontroller is run at 4MHz. Watchdog timer is enabled. The prescaler is assigned to timer0 so WDT will time out in 18ms (nominal value as per datasheet). In the firmware WDT is cleared during the polling of TMR0. &lt;br /&gt;&lt;br /&gt;With the single 8-bit timer I wanted to get the highest possible tick. At 4Mhz--which translates to a million instructions/sec--that would be 65.536ms with TMR0 = 0 and a prescale of 1:256. But that's not a good tick value. 50ms is easier and more sensible to work with. To obtain that, prescale is set to 1:256 and TMR0 reinitialized to 0x3D (decimal 61) every time it rolls over to zero. Given those values TMR0 will overflow every (256 - 61)(256)(1us) = 49.92ms. &lt;br /&gt;&lt;br /&gt;Remember baseline PICs don't have interrupts. We have to poll the timer to determine when it's overflowed. But for some reason using the conditional "while(TMR0 != 0)" doesn't work. Using any value other than zero, however, works. So in the firmware below I have "while(TMR0 != 0xFF)" instead. With that conditional perhaps I should initialize TMR0 to 0x3C instead. &lt;br /&gt;&lt;br /&gt;What I wanted was for the time interval between wipes to be increments of 1 second, that is, as the user turns the potentiometer the interval would get longer/shorter but it would jump from 1 second to 2 to 3 and so on, and not fractions thereof. As for maximum interval, I initially specified 60sec but then I settled on 30. Minimum interval would be 1sec. &lt;br /&gt;&lt;br /&gt;Note that time interval is defined as the time between de-energization of the REL2 and its re-energization. &lt;br /&gt;&lt;br /&gt;In order to realize the above specs, it occured to me there's a simple and elegant way of doing so. The ADC onboard the 10F222 has an 8-bit resolution. That's 256 values, from 0 to 255. Well what we can do is to degrade the resolution to less than 8 bits. That's easily done by right shifting the ADC value (the number the ADC spits out after the conversion). Shift it right twice and we have a 6-bit number--from 0 to 63. Or shift thrice and we a 5-bit value--0 to 31. We can then take those numbers as the time interval (in seconds). But since we specified a minimum of 1 second, we should add 1 to the 5-bit ADC value giving us a range of 1 to 32. Now keep in mind this is the number of seconds. Our timer tick, however, is ~50ms. To get the time interval in terms of ticks we have to multiply the ADC value by 1sec/0.05sec = 20. Therefore, to count 32 seconds, we need to wait for 32(20) = 640 ticks. &lt;br /&gt;&lt;br /&gt;The potentiometer is read by the ADC every timer tick. The time interval setting is therefore updated 20 times a second, except when a wipe action is being executed--the ADC is not reading the pot during that time. &lt;br /&gt;&lt;br /&gt;The firmware is such that, every timer tick, it compares the latest time interval chosen by user and the amount of time elapsed since the last wipe. If time elapsed already exceeds the latest time interval setting, then a wipe is immediately performed and the 16-bit counter monitoring the amount of time elapsed is reset to zero. &lt;br /&gt;&lt;br /&gt;For example if the pot was previously set to a time interval of say 25 sec, and 15 seconds has already elapsed, and the user has just turned the pot down to 12sec, the firmware will immediately issue a wipe action and reset the time elapsed counter. (In reality, with a read rate of 20x a second--somewhat faster than the driver can turn the pot--firmware will see the pot setting crossing the 15sec mark and issue a wipe action at that point). On the other hand, if the user had turned up the pot instead, to say 30sec., then the firmware will count all the way to 30sec before the next wipe. &lt;br /&gt;&lt;br /&gt;When powered up an initial wipe is made. Thereafter, when the succeeding wipes will occur will depend on the pot setting. The length of time I've chosen for the wiper to be triggered (turned on briefly) is one second (20 timer ticks). As mentioned in Part 2 the time for the wiper to go from parking position and back was measured to be ~1.3sec. Trigger time must be less than this. A trigger time of around half a second would probably be sufficient. Keep in mind, however, that time interval between wipes is affected by wiper trigger time.&lt;br /&gt;&lt;br /&gt;And here's the final firmware to drive the micrcontroller. Compiler used is the Hi -Tech C Lite v9.80. As I &lt;a href="http://electromotiveforces.blogspot.com/2011/06/bumped-into-hi-tech-c-compiler-bug.html"&gt;mentioned&lt;/a&gt; a few days ago the 10F222 include file of v9.81 doesn't define TRIS and so will fail to compile the following.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;/*&lt;br /&gt;&lt;br /&gt;Variable intermittent wiper control for the Toyota Innova, 2008, E-variant&lt;br /&gt;June 2011&lt;br /&gt;&lt;br /&gt;Processor = PIC 10F222 &lt;br /&gt;Compiler = Hi Tech C v.9.80&lt;br /&gt;&lt;br /&gt;Description: User adjusts a potentiometer to desired interval between wipes.&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;htc.h&amp;gt;&lt;br /&gt;#include &amp;lt;et_include.h&amp;gt;&lt;br /&gt;&lt;br /&gt;__CONFIG (OSC_4MHZ &amp;amp; MCPUDIS &amp;amp; WDTEN &amp;amp; UNPROTECT &amp;amp; MCLRDIS);        // for Hi-Tech C v9.80&lt;br /&gt;// __CONFIG (IOSCFS_4MHZ &amp;amp; MCPU_OFF &amp;amp; WDT_OFF &amp;amp; CP_OFF &amp;amp; MCLRE_OFF); // for Hi-Tech C v9.81&lt;br /&gt;&lt;br /&gt;#define   wiper              GP2       // switches 2N7000 MOSFET transistor which then switches relay which switches wiper&lt;br /&gt;#define   wipertrigtime      20        // amount of time to power up wiper. time is in terms of number of timer0 overflows&lt;br /&gt;#define   t0_init            61        // initial value of timer0. with prescaler = 1:256, overflow occurs every ~50ms &lt;br /&gt;// #define  _XTAL_FREQ       4000000 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// potentiometer setting is converted to 5-bit digital value&lt;br /&gt;// 10F222 ADC has 8bit resolution, therefore ADC value is right shifted three places&lt;br /&gt;// After right shifting, add 1 so that with ADC value of zero (potentiometer at zero ohms) the minimum time interval is 1sec.&lt;br /&gt;// Since max ADC value for 5-bit is 31, max time interval = 31 + 1 = 32sec&lt;br /&gt;int8 ADC()          &lt;br /&gt;{&lt;br /&gt;  GODONE = 1;                          // start ADC conversion&lt;br /&gt;  while(GODONE);                       // wait till GODONE bit has been cleared, signifying completion of conversion &lt;br /&gt;  return ((ADRES &amp;gt;&amp;gt; 3) + 1);     // degrade to 5 bits, and force min time interval = 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// wiper lo speed is triggered (switched on) for a brief moment, determined by constant "wipertrigtime" &lt;br /&gt;// after triggering, parking system on wiper motor will take over powering the motor until it reaches parking position &lt;br /&gt;// when wiper reaches parking position, motor stops automatically &lt;br /&gt;// NOTE: WDT must be cleared within 18ms (no prescaler assigned to it)&lt;br /&gt;void WIPE()&lt;br /&gt;{&lt;br /&gt;  int8 i;&lt;br /&gt;  wiper = on;&lt;br /&gt;  for (i=0; i&amp;lt;wipertrigtime; i++)&lt;br /&gt;  {&lt;br /&gt;    TMR0 = t0_init;&lt;br /&gt;    while(TMR0 != 0xFF)      // for yet unknown reason conditional doesn't work with "TMR0 != 0x0"&lt;br /&gt;      CLRWDT();&lt;br /&gt;  }&lt;br /&gt;  wiper = off;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// NOTE: WDT must be cleared within 18ms (no prescaler is assigned to it)&lt;br /&gt;void main()&lt;br /&gt;{&lt;br /&gt;  static int16 TIME = 40;    // number of timer0 overflows (every 50ms) between wiper sweeps. initialize to minimum time interval betwen wipes (2sec. which is equivalent to 40 timer0 overflows) &lt;br /&gt;  static int16 COUNTER = 0;  // stores the number of times timer0 has overflowed&lt;br /&gt;&lt;br /&gt;  TRIS = 0b1;                // pot is connected to AN0/GP0&lt;br /&gt;  OPTION = 0b11000111;       // wake up pin change disabled, pull ups disabled, timer0 uses internal clock, timer0 prescale = 1:256 &lt;br /&gt;  WIPE();                    // have wiper sweep windshield once upon power up&lt;br /&gt;  TMR0 = t0_init;&lt;br /&gt;  ADCON0 = 0b10000001;       // GP0/AN0 is analog, GP1 is digital, channel 0 selected, ADC on&lt;br /&gt;  &lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    while(TMR0 != 0xFF)      // poll TMR0 because there is no timer0 interrupt flag. for yet unknown reason conditional doesn't work with "TMR0 != 0x0"  &lt;br /&gt;      CLRWDT();              // WDT must be cleared within 18ms (no prescaler assigned to it)&lt;br /&gt;&lt;br /&gt;    if (COUNTER++ &amp;gt;= TIME)&lt;br /&gt;    {&lt;br /&gt;      WIPE();&lt;br /&gt;      COUNTER = 0;           &lt;br /&gt;    }&lt;br /&gt;    TMR0 = t0_init;&lt;br /&gt;    &lt;br /&gt;    TIME = (ADC())*20;       // Each value of the 5-bit ADC reading is equivalent to number of seconds. &lt;br /&gt;                             // This is then multiplied by 20 since timer0 overflows every 50ms and 1sec/0.050sec = 20&lt;br /&gt;                             // if new TIME &amp;lt; previous TIME and current COUNTER value exceeds new TIME then a wipe will occur right after the next TMR0 overflow&lt;br /&gt;                             // if new TIME &amp;gt; previous TIME then whatever COUNTER is, the next wipe will occur at a later time.   &lt;br /&gt;&lt;br /&gt;  } // while(1) &lt;br /&gt;} // void main()&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I field tested the circuit to check if everything works. It does! Here are a couple of pics of that test process. The breadboard is in the translucent plastic box while the relays are mounted on a DIN rail. REL1 and REL2 are both Telemecanique RXM2AB2JD relays. In the PCB version I will be using a small PCB mounted relay for REL2. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-2zHYCLUI8C4/TfD047lzanI/AAAAAAAAAmw/ArR6WwOtF18/s1600/innova-live-test1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/-2zHYCLUI8C4/TfD047lzanI/AAAAAAAAAmw/ArR6WwOtF18/s400/innova-live-test1.png" width="300" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ftDg3n2xJcA/TfD04Sz7qFI/AAAAAAAAAmo/lrrwwoF8ujo/s1600/innova-live-test3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/-ftDg3n2xJcA/TfD04Sz7qFI/AAAAAAAAAmo/lrrwwoF8ujo/s400/innova-live-test3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-XttK_madt8s/TfD03nDPv2I/AAAAAAAAAmg/gVREHofn6pw/s1600/innova-live-test2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-XttK_madt8s/TfD03nDPv2I/AAAAAAAAAmg/gVREHofn6pw/s400/innova-live-test2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;There's &lt;a href="http://hokum-balderdash.blogspot.com/2011/06/my-very-first-orb.html"&gt;another shot&lt;/a&gt; of this setup in which something very interesting inadvertently appeared.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;June 12 edit: A &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_12.html"&gt;Part 4&lt;/a&gt; just appeared. And because of new developments so will a Part 5.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-436460014898311465?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/436460014898311465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_10.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/436460014898311465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/436460014898311465'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_10.html' title='Variable intermittent wiper control - Part 3'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-ALWs6QC7T3E/Te97Y8fy7uI/AAAAAAAAAmI/LHWLfoRxXng/s72-c/innova-new-wires.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-5220027856603758603</id><published>2011-06-08T20:33:00.001+08:00</published><updated>2011-06-10T01:54:06.223+08:00</updated><title type='text'>Variable intermittent wiper control - Part 2</title><content type='html'>&lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control.html"&gt;Variable intermittent wiper control - Part 1&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Before delving into the electrical characteristics of the Innova's wiper system, let me just provide the wipe time. Using a stopwatch the time for the wiper in intermittent mode to make one complete sweep--wiper goes from park position, sweeps the windshield and back to parking--is approximately 1.3sec. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here again is a shot of the header pins into which the connectors plug.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-J1zcezyH7j0/TezV-LABhuI/AAAAAAAAAlI/9rz8jLdxwcM/s1600/w-headers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-J1zcezyH7j0/TezV-LABhuI/AAAAAAAAAlI/9rz8jLdxwcM/s320/w-headers.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;After testing the wiper switch module here are the pin assignments I came up with:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-abaPhU9L6uQ/TezVPVhtqpI/AAAAAAAAAlA/GzopLhpFrpQ/s1600/innova-pin-assignments.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="237" src="http://1.bp.blogspot.com/-abaPhU9L6uQ/TezVPVhtqpI/AAAAAAAAAlA/GzopLhpFrpQ/s400/innova-pin-assignments.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Testing included using the resistance/continuity mode of a multimeter to check which pins are electrically connected (practically zero ohms) while trying the various switch settings. Testing also involved plugging the connectors into the module--only partially so I can have access to the headers--turning the ignition key until power is supplied to the wiper circuit and probing the headers with the DMM and trying out the different switch settings on the stalk.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The following summarizes the cold test using the ohmmeter. The presence of links (lines) between pins means those pins are electrically connected at the indicated switch setting.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-djGyJi-kg4k/TezYmSMEzqI/AAAAAAAAAlg/t-eRUXniaUs/s1600/innova-switch-contact-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="237" src="http://4.bp.blogspot.com/-djGyJi-kg4k/TezYmSMEzqI/AAAAAAAAAlg/t-eRUXniaUs/s400/innova-switch-contact-1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-B0mfq4b_z88/TezYmWmrznI/AAAAAAAAAlY/6eIufQJaJ5w/s1600/innova-switch-contact-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://3.bp.blogspot.com/-B0mfq4b_z88/TezYmWmrznI/AAAAAAAAAlY/6eIufQJaJ5w/s400/innova-switch-contact-2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-2NKvAPR8GsQ/TezYmNNU8kI/AAAAAAAAAlQ/OUUwS_nE7cM/s1600/innova-switch-contact-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://3.bp.blogspot.com/-2NKvAPR8GsQ/TezYmNNU8kI/AAAAAAAAAlQ/OUUwS_nE7cM/s400/innova-switch-contact-3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Below are results of the hot test. All voltage values are with reference to chassis ground. The voltage on some pins jump from one value to another or span a range. This is indicated by the nomenclature "x - y". So "0 - 12V" might mean that voltage toggles between these values or that it smoothly but very quickly rises to 12 or declines to 0. Using a DMM did not allow me to determine whether the readings I was getting were indicative of discontinuous or continuous values. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-xUDiahhUFLE/TezYwuXFLGI/AAAAAAAAAlw/8B0RioaZmGI/s1600/innova-switch-volts-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://2.bp.blogspot.com/-xUDiahhUFLE/TezYwuXFLGI/AAAAAAAAAlw/8B0RioaZmGI/s400/innova-switch-volts-1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-b6MLQdgH2-U/TezYwdDOZpI/AAAAAAAAAlo/CaUuEGarrpE/s1600/innova-switch-volts-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://2.bp.blogspot.com/-b6MLQdgH2-U/TezYwdDOZpI/AAAAAAAAAlo/CaUuEGarrpE/s400/innova-switch-volts-2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I also checked the PCB disconnected from the connectors and using an ohmeter had the following results. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qzqfQhdhbog/Te0DxBGYWgI/AAAAAAAAAl4/nH-gOG_LfsE/s1600/innova-pcb-labeled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-qzqfQhdhbog/Te0DxBGYWgI/AAAAAAAAAl4/nH-gOG_LfsE/s400/innova-pcb-labeled.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The yellow box denotes the location of relay on the other side of the board. Hence the solder points within that outline will be the pins on the relay. The solder joint marked "COIL" is inferred from the fact that there is no other pin for the relay coil. Moreover resistance reading from VDD to COIL is 240 ohms--that's within the ballpark for a 12VDC coil. &lt;br /&gt;&lt;br /&gt;What is perplexing to me is that there are two relay pins to which W-PP is connected. Why?! This finding has really been very disconcerting. I would've thought that one of the relay contacts would be hooked up to W-LO. Interestingly, if you look at the W-PP track on the left side you'll see that it leads to the top portion of the board to a solder joint. So W-PP is connected to some other component. The mystery deepens. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;With the module disconnected and the ignition key turned to supply power to the wiper (with the engine off), I used a DMM set to current measurement to act as a jumper and to measure peak current as well. Here are the results.&lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="5" cellspacing="0"&gt;&lt;caption&gt;Using DMM probes as jumpers between Net 1 and 2 with a Fluke 87V DMM set to measure current, Min-Max mode on &lt;/caption&gt; &lt;tbody&gt;&lt;tr&gt; &lt;th&gt;Net 1&lt;/th&gt; &lt;th&gt;Net 2&lt;/th&gt; &lt;th&gt;Result of connecting jumper&lt;/th&gt; &lt;th&gt;Maximum Measured Current (amperes) (see footnote)&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-VDD&lt;/td&gt; &lt;td&gt;W-LO&lt;/td&gt; &lt;td&gt;front wiper low speed on continuously but stops as soon as jumper removed--wiper does not park automatically&lt;/td&gt; &lt;td&gt;3.1 to 4.0A&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-PP&lt;/td&gt; &lt;td&gt;W-LO&lt;/td&gt; &lt;td&gt;with wiper initially NOT at parking position, the wiper will move to parking position and then stop; keeping jumper connected does not result in any further action&lt;/td&gt; &lt;td&gt;3.7 to 6.2A&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-VDD&lt;/td&gt; &lt;td&gt;W-HI&lt;/td&gt; &lt;td&gt;front wiper high speed on continuously but stops as soon as jumper removed--wiper does not park automatically&lt;/td&gt; &lt;td&gt;8.9 to 9.2A&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-RW&lt;/td&gt; &lt;td&gt;W-GND&lt;/td&gt; &lt;td&gt;rear wiper turns on continuously and parks automatically when jumper removed&lt;/td&gt; &lt;td&gt;1.4 to 1.7A&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-RM&lt;/td&gt; &lt;td&gt;W-GND&lt;/td&gt; &lt;td&gt;rear mist on as long as jumper connected&lt;/td&gt; &lt;td&gt;2.0 to 2.6A&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;W-FM&lt;/td&gt; &lt;td&gt;W-GND&lt;/td&gt; &lt;td&gt;front mist on (wiper does not turn on) as long as jumper connected&lt;/td&gt; &lt;td&gt;2.2 to 2.5A&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Note: Current measurements were made several times. Only MAX values as measured by the 87V were recorded. The range of values per item above are the lowest and highest MAX values. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Given all the above data I have drawn up a provisional schematic diagram. However, it  almost certainly is incomplete and contains errors so I won't put it up lest some reader relies upon it as gospel truth. &lt;br /&gt;&lt;br /&gt;What can be inferred with high confidence at this time are the following :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Switching the stalk to LO disconnects W-PP from W-LO and connects W-VDD and W-LO, feeding +12V to the wiper motor's low speed circuit.&amp;nbsp;&lt;/li&gt;&lt;li&gt;When the stalk is returned to OFF, W-PP is once again connected to W-LO thus automatically parking the wiper. &lt;/li&gt;&lt;li&gt;When stalk is switched to INT W-PP remains connected to W-LO. The electronic circuit comes into play and switches the on-board relay for about a second every couple of seconds. Judging from the clicks of the relay it is being turned off right at the moment of wiper parking (which makes me wonder yet again how W-PP is being used by the PCB). &lt;/li&gt;&lt;li&gt;When the stalk is switched to HI,  W-PP is not connected to W-LO. W-VDD is connected to W-HI thus supplying +12V to the wiper motor's high speed circuit. &lt;/li&gt;&lt;li&gt;When stalk is pushed all the way up (switched to MIST--this is a mislabel on Toyota's part since it doesn't turn on the spray) the spring-loaded switch is engaged and W-PP is disconnected from W-LO while W-VDD is connected to W-LO thus supplying +12V to the wiper motor's low speed circuit. Releasing the stalk returns it to OFF position and thus W-PP gets connected to W-LO and the wiper parks automatically.&lt;/li&gt;&lt;li&gt;From the OFF position, when stalk knob is turned counterclockwise (i.e., turning it toward the driver) to the rear windshield spray icon, W-RM is grounded (connected to W-GND) turning on rear mist motor.&lt;/li&gt;&lt;li&gt;From the off position, when stalk knob is turned clockwise (i.e., turned toward the dashboard) one notch to ON, W-RW is grounded (connected to W-GND) switching on the rear wiper motor.&lt;/li&gt;&lt;li&gt;With the rear wiper on, when the stalk knob is turned clockwise to the second rear windshield spray icon, both W-RW and W-RM are connected to W-GND, hence both rear wiper and rear mist are switched on. This is a spring-loaded setting and releasing the knob will return the knob to the rear-wiper-on position and the rear mist motor will be switched off. &amp;nbsp;&lt;/li&gt;&lt;li&gt;When spring-loaded stalk is pulled toward the driver W-FM is connected to ground via W-GND thus turning on the front mist motor. Because W-FM is connected to the PCB (see labeled PCB pic above), the circuit board most probably senses the level change from 12 to 0 volts. After a brief delay (~0.5sec) from the start of the mist the relay is turned on hence switching on the low speed motor circuit. The relay is kept on for several seconds so that the wiper makes at least three wipes.&amp;nbsp; &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_10.html"&gt;Part 3&lt;/a&gt; I'll discuss a microcontroller-based variable intermittent wiper circuit for the Innova and provide a full listing of the firmware.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-5220027856603758603?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/5220027856603758603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_08.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5220027856603758603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/5220027856603758603'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_08.html' title='Variable intermittent wiper control - Part 2'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-J1zcezyH7j0/TezV-LABhuI/AAAAAAAAAlI/9rz8jLdxwcM/s72-c/w-headers.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-652812955397826126</id><published>2011-06-07T21:03:00.001+08:00</published><updated>2011-06-08T20:35:00.781+08:00</updated><title type='text'>Variable intermittent wiper control - Part 1</title><content type='html'>One of my bugbears with cars I've driven is that their wiper system either don't t have an intermittent mode or if they do then they're fixed, i.e., the wiper sweeps the windshield approximately once every couple of seconds and there's no way of adjusting that time interval. Having an intermittent setting is better than none but having a variable intermittent mode is certainly much better. With a very light drizzle the windshield may need only need be wiped every 10 to 20 seconds, hence having a wiper control that has a user-adjustable wipe interval from one second to say one minute eliminates the hassle of having to turn off the wiper and then on again every now and then. &lt;br /&gt;&lt;br /&gt;Some 8 years ago I designed and installed my first variable intermittent wiper control. It was a simple circuit based on the 555 timer configured as an &lt;a href="http://en.wikipedia.org/wiki/555_timer_IC"&gt;astable multivibrator&lt;/a&gt;. It employed a potentiometer to allow adjustment of the wipe interval. The control circuit switched a relay which in turn switched the wiper motor on and off. It even had green and red LEDs indicators showing when the relay was on/off. By 2007 I had a preliminary microcontroller design but never got around to working on it.&lt;br /&gt;&lt;br /&gt;Here's the Honda Civic's wiper stalk switch schematic. As indicated the relay contacts were inserted in series after cutting the parking position wire.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-VvGvoHZbDgA/TezNHEa3rvI/AAAAAAAAAk4/kuciXI3Xfy4/s1600/honda-civic-wiper-schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://2.bp.blogspot.com/-VvGvoHZbDgA/TezNHEa3rvI/AAAAAAAAAk4/kuciXI3Xfy4/s320/honda-civic-wiper-schematic.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And here's the 555 circuit to implement the variable intermittent control. I used an aluminum electrolytic capacitor for the timing cap. But as is known aluminums are leaky. Using a tantalum would be better. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-vurkoKJtqEw/TezNGzi1E7I/AAAAAAAAAkw/N2cyPObzpbA/s1600/555-intermittent-wiper-sche.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://3.bp.blogspot.com/-vurkoKJtqEw/TezNGzi1E7I/AAAAAAAAAkw/N2cyPObzpbA/s320/555-intermittent-wiper-sche.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The circuit provides a momentary pulse to the wiper motor (less than a second) after which the motor latches--it powers itself through the parking position circuit--and then stops when it reaches the parking position where the wiper blades are back at the bottom of the windshield.. &lt;br /&gt;&lt;br /&gt;R3 is a 1Mohm potentiometer. Because of D1 (1N4148 signal diode) charging rate is much faster than discharge rate, charging only through R1. Theoretically, output high and low times are as follows:&lt;br /&gt;&lt;br /&gt;T&lt;sub&gt;high&lt;/sub&gt; = 0.693(R1)(C1) = 0.693(7.5Kohm)(100µF) = 0.52sec&lt;br /&gt;&lt;br /&gt;T&lt;sub&gt;low&lt;sub&gt;min&lt;/sub&gt;&lt;/sub&gt; = 0.693(R2 + R3&lt;sub&gt;min&lt;/sub&gt;)(C1) = 0.693(15K + 0)(100µF) = 1.04sec&lt;br /&gt;&lt;br /&gt;T&lt;sub&gt;low&lt;sub&gt;max&lt;/sub&gt;&lt;/sub&gt; = 0.693(R2 + R3&lt;sub&gt;max&lt;/sub&gt;)(C1) = 0.693(15K +1Mohm)(100µF) = 70.34sec&lt;br /&gt;&lt;br /&gt;D3 is a &lt;a href="http://en.wikipedia.org/wiki/Flyback_diode"&gt;flyback diode&lt;/a&gt; to dissipate the reverse emf developed across the relay coil when power to it is cut off. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;An intermittent wiper control is relatively easy to design. The problem and headache is the cars themselves, specifically, figuring out which wires of the wiper switch go where and control what, and how sophisticated or primitive the installed wiper switch module is. A non-electronic wiper control as is found in the 90's Honda Civic is relatively straightforward. As can be seen above it only has mechanical switches and has very few settings--low speed, high speed, water spray, and momentary wipe (a spring loaded switch lets the driver turn on the wiper which turns off once the stalk is released).&lt;br /&gt;&lt;br /&gt;Designing a control for a wiper system that already has an intermittent mode and employs electronics and which has a rear wiper-mist is a tad more difficult--because it takes more effort to analyze and reverse engineer the lot of switches and circuitry--something absolutely necessary because we don't want to fry the car's electronics and cause some major short circuit! &lt;br /&gt;&lt;br /&gt;I've been meaning to install a variable intermittent control on the &lt;a href="http://en.wikipedia.org/wiki/Toyota_Innova"&gt;Toyota Innova&lt;/a&gt; for some time now but for fear of ruining the wiper system, or worse, zapping some other part of the electrical system--say, the very expensive car computer!--I've procrastinated even inspecting the wiper system to determine whether the project would be feasible. To add to the concerns, this car has an airbag on the steering wheel. Inadvertently triggering it was certainly on my mind. But last week, with trepidation, I finally dove right in.&lt;br /&gt;&lt;br /&gt;To begin with I had to get to the wiper stalk's base--which was hidden within the steering column--to get to the wires leading in and out of it so as to be able to test them. As it turns out, removing the plastic covers surrounding the steering column was easy--the top and bottom halves simply snap onto each other and are held down on the steering with just two screws (one of which actually screws onto the detachable wiper switch module--strange I thought).&lt;br /&gt;&lt;br /&gt;With the base of the stalk exposed two wire connectors became apparent. Pushing down on the retaining clips on the connectors permitted their removal from the wiper switch module. After some time studying the module I was able to remove it from the steering column--It had plastic "rails" onto which it slid in and one retaining clip was all that held it in place. Love that simple plug-in design. Here's a pic after I'd taken the wiper switch module off. The two white rectangular wire connectors dangling beneath the steering wheel are conspicuous. Those red and black wires and alligator clips are from my DMM.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-0QuNYjnC-So/TewiOI6VT6I/AAAAAAAAAiw/i3sJeGTKJAI/s1600/w-steering-ignition.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-0QuNYjnC-So/TewiOI6VT6I/AAAAAAAAAiw/i3sJeGTKJAI/s320/w-steering-ignition.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Close up of the connectors: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Cdt3SvzTsg0/Tewlm1uKLCI/AAAAAAAAAkA/VrI2CZeWR9M/s1600/w-connector-wires.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-Cdt3SvzTsg0/Tewlm1uKLCI/AAAAAAAAAkA/VrI2CZeWR9M/s320/w-connector-wires.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-zzud4sU6jkk/TewlnaXjZcI/AAAAAAAAAkI/1Lm4fTZp-Yc/s1600/w-connectors.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-zzud4sU6jkk/TewlnaXjZcI/AAAAAAAAAkI/1Lm4fTZp-Yc/s320/w-connectors.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And here are shots of the the module:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-w5Co0BOXmE0/TewjD7Y50zI/AAAAAAAAAi4/cLia9TLFXLA/s1600/w-module-top.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-w5Co0BOXmE0/TewjD7Y50zI/AAAAAAAAAi4/cLia9TLFXLA/s320/w-module-top.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Gu6dWYV8CRU/TewjEKFXO3I/AAAAAAAAAjA/OCIODvEMpXk/s1600/w-module-side.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-Gu6dWYV8CRU/TewjEKFXO3I/AAAAAAAAAjA/OCIODvEMpXk/s320/w-module-side.png" width="320" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-q5jL3fY8TWU/TewjEcE44uI/AAAAAAAAAjI/yErq94xxwNo/s1600/w-module-bottom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-q5jL3fY8TWU/TewjEcE44uI/AAAAAAAAAjI/yErq94xxwNo/s320/w-module-bottom.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I was able to partially disassemble the module and here's how the sub units look:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-s8LxYTfmECg/TewkTDzNEYI/AAAAAAAAAjQ/K8JRVTPKmbw/s1600/w-disass-top.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-s8LxYTfmECg/TewkTDzNEYI/AAAAAAAAAjQ/K8JRVTPKmbw/s320/w-disass-top.png" width="320" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-RYnbQYvp2t0/TewkUFhox1I/AAAAAAAAAjg/26sKH0bwFqs/s1600/w-disass-top-zoom-b.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-RYnbQYvp2t0/TewkUFhox1I/AAAAAAAAAjg/26sKH0bwFqs/s320/w-disass-top-zoom-b.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-0imFq9D7sZg/TewkTnhggMI/AAAAAAAAAjY/_SoMXh2RQjM/s1600/w-disass-top-zoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-0imFq9D7sZg/TewkTnhggMI/AAAAAAAAAjY/_SoMXh2RQjM/s320/w-disass-top-zoom.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Turning the parts over we have:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-iZb86N637tw/TewlAODgxEI/AAAAAAAAAjo/YH_9bN8Uh8k/s1600/w-disass-bottom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-iZb86N637tw/TewlAODgxEI/AAAAAAAAAjo/YH_9bN8Uh8k/s320/w-disass-bottom.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-3RNa-BW8BEc/TewlBPD_uOI/AAAAAAAAAj4/PJnjXyv8YVM/s1600/w-disass-bott-right.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-3RNa-BW8BEc/TewlBPD_uOI/AAAAAAAAAj4/PJnjXyv8YVM/s320/w-disass-bott-right.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-MRUvh0MdG-4/TewlAgyavDI/AAAAAAAAAjw/dHt7ZZEQCLo/s1600/w-disass-bott-left.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-MRUvh0MdG-4/TewlAgyavDI/AAAAAAAAAjw/dHt7ZZEQCLo/s320/w-disass-bott-left.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;See all the copper "tracks" embedded in the plastic? They're thin copper inserts some of which lead to the headers and some to PCB on the other side.&amp;nbsp; The yellow-greenish goo is lubricant for the sliding contacts. &lt;br /&gt;&lt;br /&gt;And the headers/pins (into which the connectors plug) on the module:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-wNtHcCD-lPk/TewmNaV-OeI/AAAAAAAAAkQ/zA8GT3jgnpA/s1600/w-headers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-wNtHcCD-lPk/TewmNaV-OeI/AAAAAAAAAkQ/zA8GT3jgnpA/s320/w-headers.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Close up of the circuit board. There's one 3-terminal SOT device near the center of the board. No markings to identify it. Other devices are surface mount resistors. Values are readily discernible. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-gByKEtx1y1o/TewmY3xF4hI/AAAAAAAAAkY/koLy-s3rQsI/s1600/w-pcb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-gByKEtx1y1o/TewmY3xF4hI/AAAAAAAAAkY/koLy-s3rQsI/s320/w-pcb.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The PCB cannot be removed because there are some seven copper "posts" (embedded in the black plastic module) which are soldered onto the PCB. Here's how those copper posts look:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ne_IpwajWcE/TewnAVgSb0I/AAAAAAAAAkg/mTbZdT8iB1M/s1600/w-pcb-copper-bar.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-ne_IpwajWcE/TewnAVgSb0I/AAAAAAAAAkg/mTbZdT8iB1M/s320/w-pcb-copper-bar.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-GI3p7yITgNM/TewnAlwBvNI/AAAAAAAAAko/iD1WtaiHwHk/s1600/w-pcb-relay.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-GI3p7yITgNM/TewnAlwBvNI/AAAAAAAAAko/iD1WtaiHwHk/s320/w-pcb-relay.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The PCB is at the top portion of the pics. They're overexposed in the images so you won't see the telltale green solder mask, just the edge of the phenolic? board. &lt;br /&gt;&lt;br /&gt;In those two images notice the large black rectangular block. That's a relay although I could not see any markings on it. There's also at least one aluminum electrolytic capacitor in there. I could see the temperature rating but not its capacitance. The only way I'll be able to view all the components would be to desolder the posts. I'm not about to do that given my dodgy de/soldering skills. Maybe if I ever get a surplus unit I can do a teardown all the way.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control_08.html"&gt;Part 2&lt;/a&gt; I'll have the results of my electrical tests of the module.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-652812955397826126?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/652812955397826126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/652812955397826126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/652812955397826126'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/variable-intermittent-wiper-control.html' title='Variable intermittent wiper control - Part 1'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-VvGvoHZbDgA/TezNHEa3rvI/AAAAAAAAAk4/kuciXI3Xfy4/s72-c/honda-civic-wiper-schematic.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7917719156733839271</id><published>2011-06-05T17:32:00.000+08:00</published><updated>2011-06-05T17:32:42.147+08:00</updated><title type='text'>It's play time with conductive playdough</title><content type='html'>Recipe for &lt;a href="http://courseweb.stthomas.edu/apthomas/SquishyCircuits/conductiveDough.htm"&gt;conductive&lt;/a&gt; and &lt;a href="http://courseweb.stthomas.edu/apthomas/SquishyCircuits/insulatingDough.htm"&gt;insulating&lt;/a&gt; playdoughs. &lt;span data-jsid="text"&gt;Their recipe is almost the same as the &lt;a href="http://www.bbc.co.uk/dna/h2g2/A1134073"&gt;bbc's&lt;/a&gt;. &lt;a href="http://www.bbc.co.uk/dna/h2g2/A1134073" rel="nofollow" target="_blank"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;  For those who want to measure by weight like I do here's the &lt;a href="http://www.newscientist.com/article/dn19522-homebrew-technology-electronics-out-of-play-dough.html"&gt;newscientist's formula&lt;/a&gt;.&lt;a href="http://www.newscientist.com/article/dn19522-homebrew-technology-electronics-out-of-play-dough.html" rel="nofollow" target="_blank"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;My main concern with the kid-safe conductive dough is that the high salt content will corrode wires, metals, and--Electra forbid--test instrument (multimeter) probes. According to &lt;a href="http://youtu.be/BhdJEO_r-Go"&gt;Anne Marie Thomas&lt;/a&gt; a 1cm long dough with a diameter of 1cm has a resistance of 80ohms. Given the &lt;a href="http://en.wikipedia.org/wiki/Electrical_resistivity_and_conductivity"&gt;equation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;R = ρ l/A &lt;br /&gt;&lt;br /&gt;where &lt;br /&gt;R = resistance in ohms&lt;br /&gt;ρ = resistivity of the material&lt;br /&gt;l = length of material&lt;br /&gt;A = area&lt;br /&gt;&lt;br /&gt;we can solve for ρ of this conductive playdough:&lt;br /&gt;&lt;br /&gt;80ohms = ρ (0.01m) / [(0.01/2)^2(3.14)]&lt;br /&gt;&lt;br /&gt;ρ = 0.63&lt;br /&gt;&lt;br /&gt;I'm not the creative type so right now I can't think of any serious use for this stuff. The material isn't stable and will dry out and probably crumble, and its mechanical and electrical properties will change in a matter of weeks so the lifespan of circuits employing it will be quite limited.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-7917719156733839271?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/7917719156733839271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/its-play-time-with-conductive-playdough.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7917719156733839271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/7917719156733839271'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/its-play-time-with-conductive-playdough.html' title='It&apos;s play time with conductive playdough'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-242005967907031947</id><published>2011-06-03T22:52:00.000+08:00</published><updated>2011-06-03T22:52:00.015+08:00</updated><title type='text'>Bumped into a Hi-Tech C Compiler bug</title><content type='html'>Am developing a circuit using the &lt;a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en023672"&gt;PIC10F222&lt;/a&gt;. Since I don't want to code in assembly I chose to use Hi-Tech C Compiler for PIC 10-12-16 (freebie version) v9.81 within Microchip's MPLAB IDE (v.8.66). I'm still developing the software but when I compiled the very short initial code I got an error message saying that "TRIS" is an "undefined identifier." The offending line that tripped the compiler was "TRIS = 0b10". I couldn't understand why it would be flagged as an error since that's the correct syntax to initialize the TRISIO register (which is not accessible directly) according to Hi-Tech User Manual (DS51865B-page 53): &lt;br /&gt;&lt;blockquote&gt;3.3.9.2 THE TRIS INSTRUCTIONS&lt;br /&gt;&lt;br /&gt;Some PIC devices use a TRIS instruction to load the TRIS register. The &amp;lt;htc.h&amp;gt; header file will ensure a special definition for a C object called TRIS. PICC will automatically use the TRIS instruction when an appropriate processor is selected and the TRIS register is accessed.&lt;br /&gt;&lt;br /&gt;For example, to make all the bits on the output port high impedance, the following code can be used.&lt;br /&gt;&lt;br /&gt;TRIS = 0xFF;&lt;br /&gt;&lt;br /&gt;This will load the appropriate value into the W register and then call the TRIS instruction. &lt;br /&gt;&lt;br /&gt;Those PIC devices which have more than one output port may have definitions for objects: TRISA, TRISB and TRISC, depending on the exact number of ports available. This objects are used in the same manner as described above.&lt;/blockquote&gt;&lt;br /&gt;Given that last paragraph I changed TRIS to TRISIO, TRISA, TRISB, and TRISC. Still got the same error. I also changed the numerical values to decimal and hex. I even closed MPLAB and opened it again. None of these licked the problem. Finally since I have v9.80 installed I tried that to compile the program. &lt;br /&gt;&lt;br /&gt;Lo and behold! The error message disappeared. When I opened the include file for the 10F22x for v.9.80 I found out that TRIS and TRISIO are defined therein, but they aren't in the 10F222 include file for v.9.81. Oddly though, while OPTION is defined in v.9.80 and isn't in v9.81, the latter nevertheless understood that command and correctly compiled it. &lt;br /&gt;&lt;br /&gt;So there. That's a bug in version 9.81. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Having opened the include files for both versions, I was vexed to find out that there are a number of differences in the defines. For instance for the configuration bits, v9.80 lists the following:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;// Configuration Mask Definitions&lt;br /&gt;/* Internal oscillator frequency select */&lt;br /&gt;#define OSC_8MHZ 0xFFF // internal osc is 8MHz&lt;br /&gt;#define OSC_4MHZ 0xFFE // internal osc is 4MHz&lt;br /&gt;&lt;br /&gt;/* Master clear pullup enable */&lt;br /&gt;#define MCPUEN  0xFFD // pullup enable&lt;br /&gt;#define MCPUDIS  0xFFF // pullup disable&lt;br /&gt;&lt;br /&gt;/*watchdog*/&lt;br /&gt;#define WDTEN  0xFFF // watchdog timer enable&lt;br /&gt;#define WDTDIS  0xFFB // watchdog timer disable&lt;br /&gt;&lt;br /&gt;/* code protection */&lt;br /&gt;#define PROTECT  0xFF7 // protect the program code&lt;br /&gt;#define UNPROTECT 0xFFF // do not protect the program code&lt;br /&gt;&lt;br /&gt;/* MCLR Pin function */&lt;br /&gt;#define MCLREN  0xFFF // master clear reset enable&lt;br /&gt;#define MCLRDIS  0xFEF // master clear reset disable&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;while v9.81 has these:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;//&lt;br /&gt;// Configuration mask definitions&lt;br /&gt;//&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// Config Register: CONFIG&lt;br /&gt;#define CONFIG               0x0FFF&lt;br /&gt;// Internal Oscillator Frequency Select bit&lt;br /&gt;// 8 MHz&lt;br /&gt;#define IOSCFS_8MHZ          0xFFFF&lt;br /&gt;// 4 MHz&lt;br /&gt;#define IOSCFS_4MHZ          0xFFFE&lt;br /&gt;// Master Clear Pull-up Enable bit&lt;br /&gt;// Pull-up disabled&lt;br /&gt;#define MCPU_OFF             0xFFFF&lt;br /&gt;// Pull-up enabled&lt;br /&gt;#define MCPU_ON              0xFFFD&lt;br /&gt;// Watchdog Timer Enable bit&lt;br /&gt;// WDT enabled&lt;br /&gt;#define WDT_ON               0xFFFF&lt;br /&gt;// WDT disabled&lt;br /&gt;#define WDT_OFF              0xFFFB&lt;br /&gt;// Code protection bit&lt;br /&gt;// Code protection off&lt;br /&gt;#define CP_OFF               0xFFFF&lt;br /&gt;// Code protection on&lt;br /&gt;#define CP_ON                0xFFF7&lt;br /&gt;// GP3/MCLR Pin Function Select bit&lt;br /&gt;// GP3/MCLR pin function is MCLR&lt;br /&gt;#define MCLRE_ON             0xFFFF&lt;br /&gt;// GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD&lt;br /&gt;#define MCLRE_OFF            0xFFEF&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;None of the bit names are the same!&lt;br /&gt;&lt;br /&gt;Since I'm using the ADC, the line "GO_nDONE = 1"--which is perfectly fine in v9.81--was flagged as an error in v9.80 because it only recognizes "GODONE." Now try compiling "GODONE = 1" in v9.81. What do you think will happen? You guessed it. You get a build fail.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;For who knows what reason, Microchip not only demands we have a photographic memory to remember all those changing (or alternate) defines, it's also made it a hair-tearing ordeal to port firmware from one compiler version to the next. This is yet another reason I really would rather stick to &lt;a href="http://www.mikroe.com/eng/products/view/7/mikroc-pro-for-pic/"&gt;mikroC&lt;/a&gt; if not for the fact that mikro doesn't support the baseline PICs and has a 2K code limit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-242005967907031947?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/242005967907031947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/bumped-into-hi-tech-c-compiler-bug.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/242005967907031947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/242005967907031947'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/bumped-into-hi-tech-c-compiler-bug.html' title='Bumped into a Hi-Tech C Compiler bug'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-4845594519733805845</id><published>2011-06-02T08:42:00.000+08:00</published><updated>2011-06-02T08:42:42.138+08:00</updated><title type='text'>Bugs in the PIC12F1822 / 1840 datasheets</title><content type='html'>I &lt;a href="http://electromotiveforces.blogspot.com/2011/05/test-driving-pic12f1822s-dac.html"&gt;mentioned&lt;/a&gt; a few days ago that there's something amiss in the datasheets for the PIC12F1822/16F1823 and PIC12F1840 regarding the DAC negative voltage source. I reported that problem to Microchip and today I received the following reply:&lt;br /&gt;&lt;blockquote&gt;In the PIC12F1822 and PIC16F1823 there is not a DACNSS bit.  The drawing (Figure 17-1) and references to this are incorrect and the register bits are correct as published.  The negative reference is always connected to Vss.&lt;br /&gt;&lt;br /&gt;The PIC12F1840 DACCON0 is incorrect and bit ‘0’ should be unimplemented.&lt;/blockquote&gt;&lt;br /&gt;There is no negative Vref pin on the chips so that should've been the clincher for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-4845594519733805845?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/4845594519733805845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/bugs-in-pic12f1822-1840-datasheets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4845594519733805845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/4845594519733805845'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/06/bugs-in-pic12f1822-1840-datasheets.html' title='Bugs in the PIC12F1822 / 1840 datasheets'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-6994405013147116330</id><published>2011-05-29T22:56:00.001+08:00</published><updated>2011-05-31T00:42:43.964+08:00</updated><title type='text'>DAC using the FVR as voltage source</title><content type='html'>Continuing with the DAC tests over the last two days, I wanted to see the DAC using the internal fixed voltage reference instead of V&lt;sub&gt;DD&lt;/sub&gt;. This would allow V&lt;sub&gt;DD&lt;/sub&gt; to vary (with the condition that V&lt;sub&gt;DD&lt;/sub&gt; &amp;gt; FVR output) without affecting the DAC operation. So I loaded the PIC18F1822 with the firmware below. Note that the internal oscillator frequency has been set to 31kHz to provide a delay between increment/decrement of DACCON1 value.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;void InitRegisters()&lt;br /&gt;{&lt;br /&gt;  PORTA = 0;&lt;br /&gt;  ANSELA = 0;&lt;br /&gt;  TRISA = 0;&lt;br /&gt;  OSCCON = 0b1000;            // run internal oscillator at 31kHz LF&lt;br /&gt;  FVRCON = 0b10001000;        // enable fixed voltage reference for DAC, set gain = 2 to give a ref volt of 2.048V&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main() &lt;br /&gt;{&lt;br /&gt;  InitRegisters();&lt;br /&gt;  DACCON0 = 0b10101000;       // DAC enabled, DAC output available on DACOUT pin, FVR as positive voltage source&lt;br /&gt;  DACCON1 = 0x1F;  &lt;br /&gt;  while(1)&lt;br /&gt;  {&lt;br /&gt;    while (DACCON1-- &amp;gt;1);     // go from DACCON1 = 31 to DACCON1 = 0 &lt;br /&gt;    while (DACCON1++ &amp;lt; 30);   // go from DACCON1 = 1 to DACCON1 = 31&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And here are the oscilloscope readings:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-aWNwqkypjdg/TeJbjEy5uDI/AAAAAAAAAik/E1dQ5T8b7X4/s1600/DAC2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="234" src="http://2.bp.blogspot.com/-aWNwqkypjdg/TeJbjEy5uDI/AAAAAAAAAik/E1dQ5T8b7X4/s320/DAC2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-X78qj6tqy1Q/TeJbixvEsmI/AAAAAAAAAic/4uFVWXbZL-Y/s1600/DAC1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="234" src="http://4.bp.blogspot.com/-X78qj6tqy1Q/TeJbixvEsmI/AAAAAAAAAic/4uFVWXbZL-Y/s320/DAC1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4092193417928690079-6994405013147116330?l=electromotiveforces.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://electromotiveforces.blogspot.com/feeds/6994405013147116330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://electromotiveforces.blogspot.com/2011/05/dac-using-fvr-as-voltage-source.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6994405013147116330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4092193417928690079/posts/default/6994405013147116330'/><link rel='alternate' type='text/html' href='http://electromotiveforces.blogspot.com/2011/05/dac-using-fvr-as-voltage-source.html' title='DAC using the FVR as voltage source'/><author><name>Edwardson</name><uri>http://www.blogger.com/profile/11988464368427565221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-aWNwqkypjdg/TeJbjEy5uDI/AAAAAAAAAik/E1dQ5T8b7X4/s72-c/DAC2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4092193417928690079.post-7387046278496503999</id><published>2011-05-29T00:18:00.000+08:00</published><updated>2011-05-29T00:18:59.047+08:00</updated><title type='text'>Checking the DAC unit on another PIC</title><content type='html'>Using the same hardware setup as in the &lt;a href="http://electromotiveforces.blogspot.com/2011/05/test-driving-pic12f1822s-dac.html"&gt;PIC12F1822 DAC test&lt;/a&gt; I dropped a PIC16F1827 into the breadboard and checked how its DAC would perform, given that the 12F1822 flunked horribly. The data below says it all. The 1827's DAC performed as it should&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="3" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;DACCON1&lt;/td&gt;&lt;td&gt;VOUT measured (volts)&lt;/td&gt;&lt;td&gt;VOUT computed (volts)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x00&lt;/td&gt;&lt;td&gt;0.008&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x01&lt;/td&gt;&lt;td&gt;0.113&lt;/td&gt;&lt;td&gt;0.106&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x02&lt;/td&gt;&lt;td&gt;0.222&lt;/td&gt;&lt;td&gt;0.213&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x04&lt;/td&gt;&lt;td&gt;0.435&lt;/td&gt;&lt;td&gt;0.427&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x08&lt;/td&gt;&lt;td&gt;0.855&
