Possible to detect a low supply voltage condition?

Does the wixel’s processor have any built in way of knowing when its supply voltage is getting lower than an acceptable threshold? I have several strategies in my app to help guard against excessively draining my battery, such as going to low current sleep mode if there is no activity detected after a specified time. But it would be nice to go a step further and immediately jump into sleep mode if a low voltage (brown-out?) condition were detected.

Perhaps I could do something like this using the A/D capability, but it would be very tricky to do that in my app because every single I/O pin is already being used. I guess I could add an external power management chip too, but the best thing would be if the CPU already has some awareness of a low voltage condition, so I could periodically read and respond to it.

Hello.

You might be able to use the internal 1.25 V source as a reference to detect a low supply voltage condition. More information on using the internal reference voltage can be found on the Analog-to-Digital Converter library page.

- Jeremy

Thanks Jeremy. based on your advice, I thought maybe the adcReadVddMillivolts() might be a good start for me, since it does not seem dependent on actually setting up a pin as an A/D input. Looking at what it returns via USB basedf diags, I’m not sure I understand its return value (about 7496 powered from the USB port). But if its a predictable number I can use for comparison, maybe it will be useful!

Now I have to rig a USB cable without its power wire, so I can watch and see how the value drops when powered by battery, as the battery voltage drops!

I think you should try to figure out why you are getting 7496 from adcReadVddMillivolts. I just double-checked the math in its implementation and it seems correct to me. It calls adcRead to read the voltage of the VDD/3 line using the 1250 mV internal reference. The maximum reading is 2047, so the answer from adcRead will be (VDD/3) * (2047/1250). Solving for VDD, we get VDD = adcRead31250/2047, which matches the code. The test_adc app in the Wixel SDK reports the return value of adcReadVccMillivolts, so maybe you could try running that.

–David

Thanks Dave. You were right… dumb programming error on my part. I get more like 3300 now, give or take.

Now I have to figure out a way to use the LEDs to indicate this value to me so i can see the reading as the wixel approaches the threshold of unacceptable low operating voltage. ! can’t use the USB to monitor, because even with a “doctored” usb cable ( that gets its VDD from a jumper back to the battery), the USB communication back to the PC drops out with a supply of about 3.8 volts. I know from my existing LED indications that my program is running down to a much lower voltage, so… back to the old flashing LED tricks!

I’ll report again when I get a better handle on this. Someone else using battery power may also wish to drop into sleep mode when a low voltage condition is sensed.

Tell me… to watch the 3v3 voltage slowly drop off, should I be recalibrating continually, as in…

while (1)
{
uint16 vddMillivolts = adcReadVddMillivolts();
adcSetMillivoltCalibration(vddMillivolts); // ???

displayVoltageSomehow(vddMillivolts );

}

OK this worked out very well! As i said in my last post, i had to create a little routine to signal the voltage to me through the LEDs, and indeed I could see and prove that the wixel could keep operating even slightly below 2 volts. This (ugly) routine can be called continually from a main() loop. It flashes 3 digits, 1/2 second apart. Make the REP_SPEED higher to make it easier to see the flashed digits. Of course this is intended to show the wixel’s 3V3 voltage when powered by a battery (or variable test supply), so the USB is normally unconnected. So the routine simply returns when the USB is connected.

#define NUM_DIGITS 3
#define REP_SPEED  60

 void monitorVoltage()
{
static uint32 timer =0;
static XDATA uint16 mv;
XDATA char report[10];
XDATA uint16 i, n;
uint32  timeNow = getMs();

if (timer == 0) timer = timeNow;

if ((timeNow - timer) < 1000L) return;
timer = timeNow;

if ( usbPowerPresent()) return;

 mv = adcReadVddMillivolts();
 adcSetMillivoltCalibration(mv);

 sprintf(report, "%-4d", mv);

 for (i = 0; i < NUM_DIGITS; i++)   // report 3 digits
	 {
	  n = (report[i] - '0');  // convert digit to number
	  while(n--)
		  {
		  LED_RED(1);
		  delayMs(REP_SPEED);
		  LED_RED(0)
		  delayMs(REP_SPEED);
		  }
	  delayMs(500);
	 }
 delayMs(500);
return;

}

But anyway, this is great! Now, in addition to an inactivity timer to force low power mode, i can further protect my battery by forcing sleep mode at some prescribed voltage. Of course I can only approximate the battery voltage at the measured 3V3 voltage level, plus .3 ~ .4 volt drop lost across the diode. But that’s fine. I can’t find exact data on it, but I imagine the radio transmission power must begin suffering anyway as the voltage goes down, so I’ll define a run time “param” variable so I can experiment and find the best cutoff.

Is there no end to this MCU’s tricks?? :slight_smile:

Hello.

Calling adcSetMillivoltCalibration is OK but is probably not necessary for you. It only has an effect on the adcConvertToMillivolts function.

–David

Well here I go again, resurrecting an old thread. My app is running on a little 200mAH LiPO cell, and I needed to warn the user when the cell was getting low. A LiPO cell charges to a max of about 4.2V, and can safely operated down to about 3.0 to avoid degrading the cell. So I was using adcReadVddMillivolts() to warn the user. It took a little experimentation to compensate for the shottky diodes, but it seemed to work.

Unfortunately the discharge curve of a LiPO cell is such that by the time it starts effecting the adcReadVddMillivolts() reading, you really don’t have much time left. It would be much better if I set up an A/D pin with a 1/2 voltage divider, to just read the VIN voltage directly. That way I can warn the user when the cell is at about 3.4V, half of which would be 1.7V ( well below the 3.3V reference).

Before I do this, I note that the Wixel already has a 100K resistor between VIN and P2-3, and I believe the wixel docs hint that the P2 pins might be internally pulled low. I don’t think the P2 pins can be used for A/D readings, but if I’m wrong please tell me, because it will make my task easier. I can just as easily re-do the math for a 100K resistor in series with the internal 20K pull down.

Hello. The P2_* pins on the Wixel cannot be used for ADC readings.

–David