[Home]About Charge Pumps

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org

Charge Pump Overview

In the most basic terms, in the LinuxCNC context, a charge pump is an HAL component that toggles a logic state at the rate of the thread that invokes it ( http://www.linuxcnc.org/docview/html/man/man9/charge_pump.9.html ). There is one input, charge-pump.enable which allows the output to toggle when no signal is connected (defaults to TRUE) or the input is TRUE. There is one output, charge-pump.out, which is either toggling TRUE/FALSE or is FALSE when the input is FALSE.

 Input     |  Output
 NC(TRUE)  |  Toggle
 TRUE      |  Toggle

Typically, the charge pump is used as a check for LinuxCNC being up and running, also known as a watchdog.

The HAL component estop_latch ( http://www.linuxcnc.org/docview/html/man/man9/estop_latch.9.html ) also contains a charge pump, but is calls it a watchdog. The watchdog toggles while ok-out is TRUE. If a fault occurs, making fault-in TRUE, the watchdog stops at the last state and won't restart until the fault is cleared and estop_latch is reset (fault-in=FALSE, ok-in=TRUE, and reset=goes FALSE to TRUE). The watchdog frequency is the same as the charge_pump would be, or 1/(2 * thread period) (example below).

For systems that use the parallel ports for axes and/or peripheral control, the parallel port pins can be in unknown states as the computer boots up and LinuxCNC loads. This can invoke unwanted or unsafe action of the machine. To fix this, the axes and peripherals can be designed to be disabled or powered off until the charge pump becomes active. For machines that use hardware signal generators, this usually isn't a problem because LinuxCNC needs to be up and running in order for LinuxCNC to communicate to the signal generator. If any part of the machine is directly controlled by a parallel port pin, then the use of the charge pump should be considered. The charge pump can also be used to check for signals that could be shorted to ground or a DC voltage, such as limit switches and sensors (verify).

The term charge pump is used because when the charge pump signal is connected to a parallel port pin, the resulting 0 and 5 Volt toggling output can be considered a stream of 5 Volt charge packets. A common way to detect the charge pump signal is to "pump" the charge packets into a circuit that collects the packets and charges up a capacitor (charge bucket). If there are enough packets to charge up the capacitor, the capacitor voltage gets high enough to trip the detector circuit's output. If the charge pump is stuck TRUE or FALSE no packets get pumped, just a constant 0 or 5 Volts result, which the detector will not pass on. When the charge pump is toggling and stable, the capacitor will charge and the detector output will change.

To use the charge pump component,

 "loadrt charge_pump" 

is added to the .hal file for your machine's configuration. This makes the component available for use. To run it, the component needs to be attached to a thread, which typically would be "base-thread" or "servo-thread", for example,

 "addf charge-pump base-thread". 

Some configurations don't use a base thread, so charge pump may be run in the servo thread, or a base or other thread may be created. The bottom line is that the charge pump frequency is determined by the thread period, so if the charge pump frequency is critical, one needs to create a thread that will match. If the frequency is not critical, choose whichever thread you prefer. Each thread period is a half cycle, so the frequency is 1/(period * 2).

Example: A common base period is 50000 nanoseconds (see your .ini file), or .00005 seconds, f = 1/(.00005 * 2) = 10kHz. A common servo-thread is 1000000 nanoseconds, or 500Hz.

The next step is to connect the charge pump signal to the outside world. Typically, the output would be connected to a parallel port pin such as,

 "net myCPname charge-pump.out => parport.0.pin-17-out". 

With this, pin 17 would toggle when LinuxCNC starts. For more control, the enable could be connected to a signal that would control it. An example is,

 "net notEstop iocontrol.0.user-enable-out => charge-pump.enable" 

to have the charge pump run except with an e-stop, or

 "net emcOn motion.motion-enabled => charge-pump.enable" 

to start the charge pump when the red machine ON/OFF button is ON in AXIS. Other signals may be more appropriate in order to include other functions or a more complex e-stop system.

estop_latch is used in a similar way to the charge_pump usage described above,

 "loadrt estop_latch"  (add "count=N" if you need more than one)
 "addf estop-latch.N"  (N = which one? 0, 1, 2..., a line is needed for each one)
 "net myWDname estop-latch.N.watchdog => parport.0.pin-17-out"
 "net ...."            (connect up rest of estop_latch)

This Tormach configuration uses an estop_latch watchdog: http://git.linuxcnc.org/gitweb?p=linuxcnc.git;a=tree;f=configs/tormach;hb=HEAD

Charge Pump Detectors

Charge pump detectors can range from the capacitor circuit mentioned above to a PLC (verify?) controller. Some examples are shown below.
(This is a work in progress and needs to be filled in. Those with information are invited to add or revise anything on this page.)

Diode/Cap/Schmitt Method

This circuit should output CP_GOOD high for a wide range of input frequencies, so may not catch an intermittent fault or change in frequency, but would also be tolerant of input noise. The 1MOhm bucket leak resistor shown will delay the output around .4 seconds (400ms) after the loss of AC input. Reducing the resistance can reduce this lag time, but will increase the minimum frequency to keep CP_GOOD high, which may not be a bad thing. The output is unstable around the frequency fault trip points, so the normal input frequency should be a fair bit away from the trip point frequency.

lm567 Tone Decoder Method

This chip could be the basis for charge pump detector with a means to control the low and high frequency trip points. A reaction time is not known. The plan is to breadboard a circuit in the near future (maybe, as of 2011/01/09).

I played with the above circuit on a breadboard but could not get a reliable output. My first guess is that this analog chip doesn't like a square wave input. A square wave has harmonics that the chip may be picking up. I may need to condition the input to look more like a sine wave. More to come.
I made another try with fairly good success.

I may have had a bad connection, or changing to a twisted pair on the input, or new component values may have helped. The output starts coming on at 8.7kHz and goes back off around 10.2kHz. The on/off transition is pretty small and stable. The output remained off at all other frequencies. Using Stepgen to produce short pulses instead of a 50% duty signal did not work well. I didn't measure reaction time, but it seemed instant, so should be less than 100ms. The VCO (voltage controlled oscillator) resistor and capacitor will need to be tuned to have the VCO run at the charge pump frequency. A potentiometer voltage divider is recommended to get the 5 Volt input signal down to .1V to .2V RMS range. The higher the input voltage the wider the output "on" region. The filter capacitor on pin 2 also is used to adjust this "on" region.

An opto-isolator chip on the input might be a good thing. The (open collector) output can sink up to 100mA, so a small mechanical relay or an SSR (solid state rely) can be connected to control mains voltage to power supplies, or DC to drivers. (See National datasheet: http://www.national.com/ds/LM/LM567.pdf )

This kit, with changes to some of the component values, may come in handy: http://www.ramseyelectronics.com/cgi-bin/commerce.exe?preadd=action&key=TD1


The PMDX-121, PMDX-122, PMDX-131, and PMDX-132 utilize an analog charge pump like the first schematic above except for the component values. They are optimized for operation with a charge pump with a minimum frequency of 100 Hz and a maximum of greater than 1 MHz. The values are chosen to require at least 3 full cycles of the charge pump signal before activation in order to ignore most BIOS and Windows boot sequences that test the parallel port.

The PMDX-125 and PMDX-126 (and probably any future products) utilize a micro-controller to count a minimum number of valid charge pump cycles before activation and to force deactivation after a fixed time interval without a charge pump cycle.


CNC4PC has a stand alone detector along with detectors on many of their breakout boards. They are specified to work with Mach's 12.5kHz pump. The detector is microcontroller based and uses a 15ms period to count pulses. Specified valid input frequencies are 3 kHz - 15.5 kHz, which corresponds to valid base periods of 32258 nanoseconds to 166666 nanoseconds.

AVR Method

I have made up a breadboard with an ATtiny2313 with software that times the interval between charge pump rising edges. The interval value is then compared to a parameter value. Any interval over the parameter is considered CP_GOOD. The reaction time to a fault condition can be less than 1ms, depending on the fault sensing algorithm. This could be very flexible. The following C file is just a first attempt. The serial connection is for getting a status display, but will move to a means for a user interface. The LEDs will will move to being a status display for normal use when the serial port is not connected.

/*  cp_serial-1a.c -- 2011/01/09 kwallace@wallacecompany.com
ATtiny2313, 10 MHz Ext. Crystal/8 scaler, Fuse Hi = DF, Lo = 7F
sudo avrdude -c usbtiny -p attiny2313 -U lfuse:w:0x7f:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

/* Prototypes */
void InitUART (unsigned char baudrate);
unsigned char ReceiveByte (void);
void TransmitByte (unsigned char data);

/* Interrupt for Timer1 Input Capture */
unsigned int cp_cnt;

  unsigned char sreg;
  sreg = SREG;  /* Save global interrupt flag */
  cli();        /* Disable interrupts */
  cp_cnt=ICR1;  /* Move the captured count for later processing */
  SREG = sreg;  /* Restore global interrupt flag */
  TCNT1=0x0;    /* Reset the counter to 0 */

/* Interrupt for Timer1 Overflow */
  PORTB = ~PORTB;         // Toggle pins on Port B

main (void)
/* --- Set up Timer1, Normal Mode, PD6 input is ICP --- */
  TCCR1B  = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);  /* enable noise cancelling, capture on rising edge, clock on clkio/1*/

/* --- Set up serial port --- */
  InitUART (7);      /* Set the baudrate to 9600 bps using a 1.25MHz (10MHz crystal/8) */
//  InitUART (12);      /* Set the baudrate to 4800 bps using a 1.00MHz (8MHz internal osc/8) */

/* Set up LED to toggle on Timer1 overflow */
  TIFR = 1<<TOV1;                //Clear TOV1, clear pending interrupts
  TIMSK = 1<<TOIE1;              //Enable Timer1 Overflow Interrupt

  DDRB = (1<<PB1)|(1<<PB0);      //Set Port B pins 0 and 1 as output
  PORTB = (1<<PB0)|(0<<PB1);     // Set port B pins 0 and 1, turn LED on PB1 off

/* Set up Input Capture */
  TIMSK|=(1<<ICIE1);             //enable input capture interrupts


  unsigned char i;
  while (1)
      if (ICR1 <= 10000)         // If the count between rising edge interrupts is
        {                        // less than this value
          TransmitByte (76);     // Then send an 'L' out the serial port
          TransmitByte (72);     // else send an 'H'
      TransmitByte(13);          // Always send a CR carriage return
      TransmitByte(10);          // and LF line feed
      for (i = 0; i < 200; i++); // delay a little. This may not be needed, lifted from another example

/* Initialize UART */
InitUART (unsigned char baudrate)
  UBRRL = baudrate;                     /* Set the baud rate */
  UCSRB = (1 << RXEN) | (1 << TXEN);    /* Enable UART receiver and transmitter */
  UCSRC = (1 << UCSZ1) | (1 << UCSZ0);  /* set to 8 data bits, 1 stop bit */

/* Read and write functions */
unsigned char
ReceiveByte (void)
  while (!(UCSRA & (1 << RXC)));  /* Wait for incoming data */
  return UDR;  /* Return the data */

TransmitByte (unsigned char data)
  while (!(UCSRA & (1 << UDRE)));  /* Wait for empty transmit buffer */
  UDR = data;  /* Start transmission */


PLC Method

I know nothing about PLC's but I suspect there are Ladder components that could be assembled into a pump detector.

System Examples


An example of a KX3 mill using a CNC4PC pump detector is here:


An example of a Mazak mill mentioning a PMDX pump detector is here:


This Tormach configuration uses an estop_latch watchdog:

(Draft 2011/01/03 Kirk Wallace)
2011/01/09 KW -- Lots of new stuff and updates
2011/01/10 KW -- Added estop_latch information

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org
This page is read-only. Follow the BasicSteps to edit pages. | View other revisions
Last edited November 2, 2012 9:26 am by Mvowles311 (diff)
Published under a Creative Commons License