Close
0%
0%

Discrete watchdog

Or how a charge pump solves a reliability problem

Similar projects worth following
OK it should have been named a "Dead Man Switch" (DMS) because actually I needed to energize a relay only when my software runs, and not in other cases.
Unfortunately my user-space software methods don't cover *all* the corner cases so a hardware approach was required !
In this case, it was solved by a few diodes, capacitors and resistors...

20161031: project is now considered "completed" but contributions and more insight are welcome :-)

As with other projects, I use Hackaday to "open source" my work, keep design notes tidy, help and inspire others... Here, I don't believe the system can be covered by copyright so I guess that no license applies.

The system:

A Raspberry Pi controls some lights and stuff. This could be any other programmable digital platform though.

The problem:

a safety relay must be released when the main program stops.

atexit() is not fired in all the cases and can't garantee perfect reliability and safety.

The approach:

An "active" system is required: it is not enough to set a GPIO pin off at the end of the program. There is no OS feature that restores the GPIO state in ALL conditions (such as emergency shutdown or i the kernel hanged).

Usually, people use a "watchdog", a piece of HW that is polled continuously and that resets the system when a timeout occurs.

In the current system, there is no need to reset the computer, only the safety relay.

A GPIO pin is already used: setting it works but clearing it is not perfect, for countless platform-specific reasons.

The idea:

Borrow from the classical watchdog paradigm with software polling.

Instead of pinging a particular memory or I/O address, the program toggles the existing GPIO pins instead of using a given signal level.

The program can generate at least 10Hz from the main loop.

The design:

Now the question is how to turn the oscillation into a level.

DC signals shouldn't pass, only AC and transients : a blocking cap (C1) is required.

A charge pump follows, with a couple of diodes (Schottky for lower drop).

At the end of the chain, the charge pumps fills a storage cap (C2) that controls the gate of a N-MOSFET.

In the middle, a diode (SD1) rectifies the current and lets it only charge the storage capacitor (think about a primitive crystal radio). Discharge goes through a high-value resistor (R1, 1M) to provide a long (and adjustable ?) RC time constant.

SD2 lets the blocking cap C1 recharge.

Did you notice the absence of freewheel diode across the relay's coil pins ? It is not needed because the MOSFET does not switch abruptly and the coil has a lot of time to release its energy.

If higher current is required, the charge pump can be extended to two stage to (almost) double the initial voltage. Since the Pi drives its pin with 3.3V, the single-stage circuit provides about 3V to the MOSFET's gate, which is enough for a small relay.

The parts values are chosen for a time constant of approximately one second. C1 could be increased to require fewer cycles until the MOSFET is fully conducting, though it also depends on the input signal's frequency.


Logs:
1. First implementation


  • 1 × 100nF ceramic capacitor
  • 1 × 1µF ceramic capacitor
  • 1 × N-MOSFET (low threshold voltage)
  • 2 × BAT85 (low drop, small signal, Schottky)

  • First implementation

    Yann Guidon / YGDES10/29/2016 at 16:11 11 comments

    My first version went quite well and I didn't have many surprises.

    First, let's talk about software.

    The Raspberry Pi's SoC doesn't allow you to write a value to a given pin. Instead, you have to write bits to a certain register to set the corresponding pin(s) to 1, and to another register to reset to 0. This is intended to remove some problems with a heavily shared I/O pins and avoid race conditions with Read/Modify/Write cycles.

    Other platforms (MIPS-based PIC32, others?) have a third register to "toggle" the pin, but the BCM2835 doesn't. Damnit.

    I wanted to write something elegant, with no IF, so I used a different approach. Since the pin's state must alternate, the register number will alternate too. Each time the pin is toggled, the variable that contains register number is then XORed with the difference between the SET and CLEAR registers.

    The output toggling should also be enabled or disabled. A second variable is created: it is cleared when the output is disabled (no toggling because x^0=x) and it is set to the difference ("delta") for toggling the input.

    (note: the following code uses my #C GPIO library for Raspberry Pi)

    /* Include my personal GPIO library */
    #define PI_GPIO_ERR  // includes errno.h
    #include "PI_GPIO.c" // includes stdio.h,
    // sys/mman.h, fcntl.h, stdlib.h - signal.h
    
    // number of the toggled pin
    #define PI_OUT_MOSFET (7)
    
    /* Toggle : alternate the pointer from RPI_GPSET0 to RPI_GPCLR0 */
    #define TOGGLE_DELTA ( RPI_GPSET0 ^ RPI_GPCLR0 )
    int toggle_var=RPI_GPCLR0, // start with pin OFF
        toggle_en=0;
    
    ...
    
    // interruptible wait (5ms) in the main loop
    nanosleep(&nanosleeptime,NULL);
    // Toggle the GPIO
    *(PI_gpio+toggle_var)= (1 << PI_OUT_MOSFET);
    toggle_var^=toggle_en;
    
    // to disable toggling:
    toggle_en=0;
    
    // to enable toggling:
    toggle_en=TOGGLE_DELTA;
    
    ...

    My code already contains a very short, interruptible delay (5ms) which creates a pretty good (slightly jittery) 100Hz signal on the selected pin.

    Jitter is not a problem here. At least I have an approximate frequency range so I can tune the rest of the circuit.

    Now, the hardware.

    I checked the program's behaviour with the scope then proceeded to assemble the charge pump.

    I used the same part as the following schematic:

    The discharge time constant is approximately 1µ×1M=1s. This means that the same charge as C2 should be reinjected several times per second, to keep its nominal voltage.

    C1 is chosen smaller to reduce the strain/current on the GPIO pin, and also because at least 10 cycles are needed to charge C2. Actually it's more like 30 to reach a good voltage, so 30Hz is like a minimum frequency. I have a 3:1 margin, that's good. The CPU can miss a few beats, and the relay has some hysteresis so the whole will not oscillate wildly in case of high CPU load.


    For the beginner, the interesting trick is that the ratio of capacity between C1 and C2 deternmines the speed of rising voltage. It's also influenced by the signal frequency. A larger C1 will charge C2 faster but also create higher currents. A higher input frequency requires a smaller capacitor.


    Discharge curve:

    The discharge is slow and no freewheel diode is required for the relay :-)

    Charge curve:

    As expected, 10 cycles bring the level to 2/3Vcc (2V). At 30 cycles, it's "almost there".

    The curve at the intermediary node:

    Now the only problem is that the BS170 barely lets current flow at Vgs=2.90.

    I have used BAT85 (small signal Schottky diodes) to minimize the losses but this is yet not enough. The BS170 would rather have 3.3V or 5V to switcth the required 65mA with the least losses possible.

    (Note: "just for safety" I left the freewheel diode from the previous iterations of the board, which has seen some serious work already)

    The normal solution is to wire 2 more diodes and capacitors to double the charge pump's voltage:

    But space was too tight so I used the other solution: get a better transistor. I could have some use for @DeepSOIC's depletion trick but he saw that the effect doesn't last :-P

    I settled for a SI4920...

    Read more »

View project log

Enjoy this project?

Share

Discussions

danjovic wrote 10/30/2016 at 12:45 point

Here is the schematic of the watchdog from YPVS controller: https://goo.gl/photos/KidSUAodBNy9LcAu7

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/30/2016 at 13:52 point

Wow, that thing is evolved ! a thyristor ?

May I post the picture here ?

  Are you sure? yes | no

danjovic wrote 10/30/2016 at 14:21 point

Of course you can post here. 

Despite the symbol from the datasheet the N13T1 is indeed an unipolar transistor. I think that is possible to use a 2N2646 instead, being the B2 connected to the Reset Pin (G) and the Emitter connected to the capacitor C3  (A).  Notice that this circuit also have a capacitor (C10)  for power on reset. 

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/30/2016 at 15:28 point

I put it in the gallery:

how could they mistake a thyristor with a transistor ? and how would it be wired ?...

  Are you sure? yes | no

K.C. Lee wrote 10/30/2016 at 16:07 point

That TR3 connection looks funny - shouldn't G & A be swapped?

  Are you sure? yes | no

jaromir.sukuba wrote 10/26/2016 at 20:13 point

I'm pretty sure this may for for many applications, if you can ensure that:

1, During proper software action the output toggles

2, During malfunctions it doesn't

Those two conditions aren't that much matter of course as it may seem. The software failure modes are really miscellaneous and sometimes the software can toggle IO pin(s) as one of the failure modes, resulting in serviced watchdog and permanent lockup. I've even heard of interesting hardware failure mode of old AT89C2051, which could - in particular power-up sequence - enter "fibrillation more", when the core wasn't executing user software and IO pins toggled at some strange pattern, perfectly refreshing external watchdog. MCU is just big state machine made of truckload of gates and flipflops, so it can enter some kind of funky state.

That is why watchdogs inside modern MCUs usually require somehow more convoluted access to refresh (specific unlock sequence to prevent runaway program to unlock it) and are often made as windows watchdogs, to prevent random toggling to refresh the watchdog.

Using watchdog properly needs usually some serious thoughts about software running the MCU and its application. From my experience, proper brown-out reset is more essential.

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/29/2016 at 16:28 point

Thanks for your insight :-)

Fortunately I don't deal with life-support devices and this primitive design "works for me" but I'll re-read your post if I ever have to design something really serious.

  Are you sure? yes | no

Eric Hertz wrote 10/26/2016 at 08:20 point

Ah hah, AC-coupling the input so that if it crashes during a high, it won't keep the mosfet active. Clever.

For some reason this circuit reminds me of the one I used to convert a PWM-signal to an analog value, centered at 0V... "charge-pump" might be the right term for it!

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/26/2016 at 09:21 point

It's a very common circuit, indeed, also used in #The Diode Clock and in crystal radios...

I might be on a charge pump rampage ! a few days ago I used a different topology for #FlappyScope :-D

  Are you sure? yes | no

Eric Hertz wrote 10/26/2016 at 10:00 point

Right...? "Charge-Pump" has been in your vernacular quite a bit lately :)

"I learned it from watching you!"

In other news... isn't a charge-pump what makes it possible to extract images from today's camera-chips? Something about a bucket-brigade made of charge-pumps, a bit beyond my grasp.

  Are you sure? yes | no

danjovic wrote 10/27/2016 at 06:21 point

YPVS controller module from Yamaha RD350 motor bikes also uses this type of watchdog. A capacitor coupled pin keeps feeding a transistor that resets a capacitor. If the program crashes the capacitor voltage starts to rise and triggers an unijunction transistor which resets the microcontroller. The capacitor discharges itself through the PUT and release the voltage on the reset line so the microcontroller can run again.

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/27/2016 at 10:17 point

@danjovic same problem, same solution :-)
Thanks for the description !

  Are you sure? yes | no

matseng wrote 10/26/2016 at 06:20 point

Nice and simple solution.  But it's more of a Dead Man's Switch than a Watchdog.  A watchdog usually resets and reboots the system upon failure. The DMS stops/prevents some action upon failure.

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/26/2016 at 09:17 point

Right, though it's a less used term and it didn't occur to me...

Too late :-)

  Are you sure? yes | no

Ted Yapo wrote 10/26/2016 at 11:56 point

 Maybe it's a "failsafe" ?

https://en.wikipedia.org/wiki/Fail-safe

I like this idea a lot. Seems like it covers most microcontroller failure modes. Is there a way (other than programming error) that your code could get stuck in and endless loop and constantly trigger the switch?  For instance, how probable is a soft-error (bit flip) due to a cosmic ray strike or local alpha decay? It's probably extremely rare, but I wonder how you can estimate what could happen.

I have been thinking about a similar problem on #TritiLED where I'm starting to build a light that runs 20 years continuously.  My first thought was to take the code image, randomly flip a bit, then see what the code does - for a microcontroller with only hundreds of bits of RAM, this is certainly doable, as long as you limit analysis to single bit errors. but I don't know how realistic that is.  I also don't know how likely a flash-memory error is under the same circumstances.

Anyway, nice idea here.  You could also add even more diodes and capacitors to drive a standard-level mosfet from low-voltage logic this way - 10V gate drive from 3.3V (or less).

  Are you sure? yes | no

Yann Guidon / YGDES wrote 10/29/2016 at 16:30 point

Exactly !

Actually, I should have designed a 2nd pump stage because the BS170 barely lets current flow at 2.9V (not enough to drive a 65mA relay).

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates