PIC18F25K50 brown-out reset caused by the ADC and FVR
Hello. I would like to report a problem with the PIC18F25K50 that really seems like a hardware bug. I have simple C code that can reproduce the problem on a 28-pin SPDIP PIC18F25K50. I also saw the problem happening on the 28-pin QFN version.
It seems like anyone trying to use the FVR and the ADC would have encountered this issue, but I looked around and didn't see any mention of it. Am I missing something?
The problem is that if the ADC's holding capacitor has a high voltage on it (VDD) and then you connect the ADC to the Fixed Voltage Reference (FVR), a brown-out reset occurs. To reproduce this, the FVR should be set to 1.024 V and the brown-out threshold should be 2.85 V. I think that the high voltage on the ADC's capacitor is flowing into the FVR and causing some problem.
The code below can be used to reproduce the problem. The comments in it explain exactly how it works and what you should see when you run it:
/* Code for the PIC18F25K50 that causes a brown-out reset by dumping a
* high voltage onto the FVR from the ADC's holding capacitor.
*
* Connections:
* RA0: Tie this pin to an LED that turns on when the pin is high.
* RA1/AN1: Don't connect anything to this pin.
* RA2/AN2: Don't connect anything to this pin.
* VDD: Connect a power source between 3 V and 5 V.
* GND: Connect to the GND of the power source.
*
* Expected behavior:
* On power up, LED blinks twice, pauses, then turns on.
*
* Actual behavior:
* On power up, LED blinks twice, pauses, blinks 4 times, pauses,
* blinks 4 times, pauses, blinks 4 times, pauses...
* This indicates that a brownout reset happens when the ADC
* is connected to the FVR.
*/
#pragma config FOSC = INTOSCIO
#pragma config CFGPLLEN = OFF
#pragma config CPUDIV = NOCLKDIV
#pragma config nPWRTEN = ON
#pragma config BOREN = ON
#pragma config BORV = 285
#pragma config nLPBOR = OFF
#define _XTAL_FREQ 1000000
#include <xc.h>
#define LED_ENABLE() (TRISA0 = 0)
#define LED(on) (on ? LATA0 = 1 : LATA0 = 0)
void delay_ms(unsigned int delay)
{
while(delay--) { __delay_ms(1); }
}
void blink()
{
LED(1);
delay_ms(100);
LED(0);
delay_ms(300);
}
void main(void)
{
LED_ENABLE();
/* Make RA1/AN1 be an analog input and drive it high. */
ANSELAbits.ANSA1 = 1;
TRISA1 = 0;
LATA1 = 1;
/* Make RA2/AN2 be an analog input and drive it low. */
ANSELAbits.ANSA2 = 1;
TRISA2 = 0;
LATA2 = 0;
/* Enable the FVR for 1.024 V operation. */
VREFCON0 = 0b10010000;
while(!VREFCON0bits.FVRST) { }
/* Check for a brown-out reset. */
RCONbits_t initialRCON = RCONbits;
RCONbits.POR = 1;
RCONbits.BOR = 1;
if (initialRCON.POR && !initialRCON.BOR)
{
/* A brown-out reset occurred. */
blink();
blink();
}
/* Charge up the ADC's holding capacitor by selecting AN1, which
* is driving high. */
ADCON0 = 0b00000101;
blink();
/* Uncommenting the line below fixes the problem, because it
* allows the ADC's holding capacitor to discharge by selecting
* AN2, which is driving low. */
// ADCON0 = 0b00001001;
blink();
delay_ms(400);
/* Select the FVR, connecting its output to the ADC's holding
* capacitor. This causes a brown-out reset if the capacitor is
* charged. */
ADCON0 = 0b01111101;
while(1)
{
LED(1);
}
}
I also posted the code here:
https://gist.github.com/DavidEGrayson/8dae6a2afeaffd44f1db.
I don't think it matters, but for completeness: I compiled that code with XC8 v1.33 on Windows 8.1, and loaded it onto a PIC using the PICkit 3. The commands I used to compile and load it were:
xc8 fvrbor.c -ofvrbor.hex --mode=free --chip=18f25k50
pk3cmd -Ffvrbor.hex -P18f25k50 -M -L
I ran the code on a simple protoboard with some wires soldered to it and a socket for the SPDIP PIC18F25K50. My board has a 0.1 uF capacitor between GND and VDD. The problem occurs regardless of whether the PICkit3 is connected to the board. In my original setup with the QFN chip, I was able to see a 100 mV dip on the VDD line that coincided with the reset, but that voltage dip is not happening with the SPDIP chip on the protoboard.
Workarounds The only real workaround I know of is to discharge the ADC's holding capacitor by selecting an analog input tied to GND. The code above shows how to do that. This PIC does not have an internal ADC channel that connects to GND, so it means that you need an analog input pin in your design that you can safely drive low. If you wanted to use every single analog input on the PIC and you had no control over their levels, you would not be able to use this workaround.
You can also make the problem stop happening by lowering the brown-out threshold from 2.85 V nominal to 2.5 V nominal, and ensuring that VDD is 4.5 V or higher. Of course, this doesn't address the root cause of the problem and it is not good to do it on systems operating at a high speed, so I wouldn't really recommend this as a workaround.
Since the ADC discharges its capacitor for two cycles at the end of a conversion, with careful timing it might be possible to switch to the FVR channel in the middle of a conversion and solve the problem that way. I have not tried that.
Setting the FVR to a higher voltage like 4.096 V might also help solve the problem, since that would be closer to 5 V. I have not tried that.
Does anyone know of a good workaround to this problem that does not require an external pin?
post edited by DavidEGrayson - 2015/10/14 16:38:10