Introduction: LCD 1602 From 3V Low Power Charge Pump Arduino or PIC

I wanted to run the 1602 LCD module with an Arduino for a device to run at 3V

The 1602 LCD module is readily available from ebay and quite cheap ( <$4 if you can wait for the free postage), but they don't run from below about 4V. Well, actually the chip does, but you can't establish enough contrast to see the lettering. If you connect the LCD at 3V, the VDO pin needs to be below 0V to get the contrast to the visible range.

I wanted a VERY low power application (see other instructables on getting the Arduino Pro Mini to run on low power - basically use the library, remove the power LED and the voltage regulator and remember to get the 3.3V version so you have a 8MHz clock) so I didn't want to use any of the available DC-DC converters - they are far too power hungry!

Next - very simple charge pump

Step 1: Simple Charge-pump Circuit

The solution was a simple charge-pump circuit running off a PWM pin of the microcontroller. This draws incredibly little current when running and gives good contrast right down to 3V.

PWM: stands for Pulse Width Modulation. Read more on this from the Arduino main site, but basically you can create an output that flips between 0 and 1 continuously. The frequency is set, but you can change how much of the cycle the output is 1 and the remainder is 0 (called the duty cycle).

How is works: When the pin is high, the current flows through the capacitor and through the diode to ground, charging up the capacitor. For a tiny period of time, a charged capacitor can act like a tiny battery, storing the charge. When the PWM pin goes to ground, it is like having a tiny 3V battery with it's positive connected to the ground - that means the negative end is sitting at -3V. The diode prevents the current flowing into the capacitor from the negative rail which would instantly discharge the capacitor.

I added the resistor because the circuit was giving too much contrast, VDO was too negative and allowed me better control of the negative voltage by varying the duty cycle.

Step 2: Controlling the Contrast With PWM

In my project, I was running it from a 3.7V lithium battery and therefore needed good contrast from about 4.3V (fully charged battery) down to 3.1V (when the battery is pretty much empty).

I had to use the PWM pin that runs at a higher frequency (check the data for your arduino) and changed the duty cycle according to battery voltage.

I found a duty cycle of about 13/256 gave the maximum contrast. If it stays high for too long, you see the strobing in the display. (and you don't get more contrast anyway)

Measure battery voltage: I used a function I found and deep apologies to the author because I have lost the reference I used. The function uses the arduino internal voltage reference as a comparison and then returns a value in mV of the battery voltage.

Adjust Contrast: I did experiment with a linear equation to dynamically set PWM duty cycle, but actually found a series of if statements gave me easier control.

long readBatt() { // for measuring battery (Vcc) voltage in millivolts (requires no pins to do so<br>  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // holds in this loop until ADC end conversion flag is set
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both
  long result = (high<<8) | low;
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

And the alteration of the charge pump duty cycle... you can see my old linear equation as well -more elegant, but tedious to alter

byte battLevel() {<br>  // returns a battery level from 0 - 5 calibrated within the critical range. ie at about 3.1V the cell is dead
  // this calculates the chargepumpDuty to maintain contrast
  //Serial.println(readBatt());
  int battV = readBatt();
  
  if (battV<3100) {
    chargepumpDuty = 13;
  }
  if (battV > 3100 && battV <3500){
    chargepumpDuty = 10;
  }
  if (battV >3500 && battV <4000){
    chargepumpDuty = 2;
  }
  if (battV >4000 && battV<4300){
    chargepumpDuty = 1;
  }
  if (battV > 4300){
    chargepumpDuty = 0;
  }
  //chargepumpDuty = constrain(-0.008*readBatt()+36,0,13); // was -0.008, see if that works better for 4.5V (fully charged
  //Serial.println(chargepumpDuty);
    return constrain(((readBatt()-3000)/180),0,5);   
    
    
}

Step 3: Final Tweeks

Then using a potentiometer and a multimeter to adjust the supply voltage from 5.0V to 3.0V and through experimentation, I found the duty cycle number that was the best compromise on display contrast over each range.

I now have a project that provides good display contrast over the full range of the battery output from FULL to EMPTY - something impossible without this circuit.

I powered the whole project from a very cheap iphone battery backup charger ($6 from ebay). I kept the mini-USB input charging circuit but connected the circuit directly to the 3.7V battery.

As an aside - you can't really run a low power application using the 5V output from these things, because the step-up circuit is very wasteful and also only runs when you are drawing sufficient current, otherwise the step-up circuit goes to sleep to preserve battery life.