Errors in PIC24F ADC reading: What's causing it? - Page 1
Forum section: electronics / general

author: dreaquil
author_rank: Regular Contributor
timestamp: April 15, 2016, 10:47:51 am
content: Hi,I've got a PIC24FJ64GA306 which I'm using to measure a voltage from an opamp. The pic has an external precision reference LTC6652. I've taken several precautions to ensure that I filter mains noise and I can confirm that it's minimal. The reading I'm getting from my ADC is 4021 to 4031 (its a 12 bit ADC) after averaging over 1024 readings effectively increasing its resolution to 22bits. I measured the voltage between the analogue pin and the analogue ground on micrcontroller and when converted I should be getting a reading of 4078. I measured this using both a FLUKE 185 and a Brymen BM869s and they both give the same result. I then figured that if the voltage on the pin matches that at the output of the opamp then the voltage difference must be being caused internally to the micro so I put the micro to sleep during the conversion and there is no difference in the reading so I'm left scratching my head as to what it could be. Code is down below if it helps.Code:/** File: MainCode.c* Author: Andre'** Created on 14 April 2016, 08:33*/#include <stdio.h>#include <stdlib.h>#include <p24FJ64GA306.h>#include <hd44780.h>#include <my_delays.h>#define _XTAL_FREQ 8000000UL#include <libpic30.h>// PIC24FJ64GA306 Configuration Bit Settings// 'C' source line config statements#include <xc.h>// <editor-fold defaultstate="collapsed" desc="CONFIGURATION SETTINGS">// CONFIG4#pragma config DSWDTPS = DSWDTPS1F // Deep Sleep Watchdog Timer Postscale Select bits (1:68719476736 (25.7 Days))#pragma config DSWDTOSC = LPRC // DSWDT Reference Clock Select (DSWDT uses LPRC as reference clock)#pragma config DSBOREN = ON // Deep Sleep BOR Enable bit (DSBOR Enabled)#pragma config DSWDTEN = ON // Deep Sleep Watchdog Timer Enable (DSWDT Enabled)#pragma config DSSWEN = ON // DSEN Bit Enable (Deep Sleep is controlled by the register bit DSEN)// CONFIG3#pragma config WPFP = WPFP63 // Write Protection Flash Page Segment Boundary (Page 52 (0xFC00))#pragma config VBTBOR = ON // VBAT BOR enable bit (VBAT BOR enabled)#pragma config SOSCSEL = ON // SOSC Selection bits (SOSC circuit selected)#pragma config WDTWIN = PS25_0 // Watch Dog Timer Window Width (Watch Dog Timer Window Width is 25 percent)#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset Enable)#pragma config WPDIS = WPDIS // Segment Write Protection Disable (Disabled)#pragma config WPCFG = WPCFGDIS // Write Protect Configuration Page Select (Disabled)#pragma config WPEND = WPENDMEM // Segment Write Protection End Page Select (Write Protect from WPFP to the last page of memory)// CONFIG2#pragma config POSCMD = HS // Primary Oscillator Select (HS Oscillator Enabled)#pragma config BOREN1 = EN // BOR Override bit (BOR Enabled [When BOREN=1])#pragma config IOL1WAY = ON // IOLOCK One-Way Set Enable bit (Once set, the IOLOCK bit cannot be cleared)#pragma config OSCIOFCN = OFF // OSCO Pin Configuration (OSCO/CLKO/RC15 functions as CLKO (FOSC/2))#pragma config FCKSM = CSDCMD // Clock Switching and Fail-Safe Clock Monitor Configuration bits (Clock switching and Fail-Safe Clock Monitor are disabled)#pragma config FNOSC = PRI // Initial Oscillator Select (Primary Oscillator (XT, HS, EC))#pragma config ALTVREF = DLT_AV_DLT_CV // Alternate VREF/CVREF Pins Selection bit (Voltage reference input, ADC =RA9/RA10 Comparator =RA9,RA10)#pragma config IESO = ON // Internal External Switchover (Enabled)// CONFIG1#pragma config WDTPS = PS32768 // Watchdog Timer Postscaler Select (1:32,768)#pragma config FWPSA = PR128 // WDT Prescaler Ratio Select (1:128)#pragma config FWDTEN = WDT_HW // Watchdog Timer Enable (WDT enabled in hardware)#pragma config WINDIS = OFF // Windowed WDT Disable (Standard Watchdog Timer)#pragma config ICS = PGx3 // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC3/PGED3)#pragma config LPCFG = OFF // Low power regulator control (Disabled)#pragma config GWRP = OFF // General Segment Write Protect (Disabled)#pragma config GCP = OFF // General Segment Code Protect (Code protection is disabled)#pragma config JTAGEN = ON // JTAG Port Enable (Enabled)// </editor-fold>// <editor-fold defaultstate="collapsed" desc="PIN DEFINITIONS">#define CURR_READ_BNC PORTBbits.RB2#define CURR_READ_SN6 PORTBbits.RB3#define FUNC_BTN PORTBbits.RB14#define MODE_BTN PORTBbits.RB15#define CURR_PASS_SN LATDbits.LATD1#define CONT_PASS_SN LATDbits.LATD2#define FAIL_SN LATDbits.LATD3#define CURR_PASS_BNC LATDbits.LATD4#define CONT_PASS_BNC LATDbits.LATD5#define FAIL_BNC LATDbits.LATD6#define TEST_EN LATDbits.LATD7#define CONT_EN2 LATDbits.LATD8#define CURR_EN_SN LATDbits.LATD9#define CONT_EN_SN LATDbits.LATD11#define CONT_OUT_SN PORTDbits.RD10#define CURR_EN_BNC LATGbits.LATG6#define CONT_EN_BNC LATGbits.LATG8#define CONT_OUT_BNC LATGbits.LATG7#define REF_DIS LATCbits.LATC14#define LCD_BCKL LATEbits.LATE6#define SN_MODE LATGbits.LATG2#define BNC_MODE LATGbits.LATG3#define SN_CH 3#define BNC_CH 2#define SN_BUF ADC1BUF3#define BNC_BUF ADC1BUF2// </editor-fold>int i=0;long voltage=0;void __attribute__((__interrupt__)) _ADC1Interrupt(void);void __attribute__((__interrupt__, auto_psv)) _ADC1Interrupt(void){IFS0bits.AD1IF= 0; //Clear the ADC1 interrupt flag or else//the CPU will keep vectoring back to the ISR}void IO_Init(){TRISDbits.TRISD7=0;//TEST_ENTRISDbits.TRISD8=0;//CONT_EN2TRISDbits.TRISD9=0;//CURR_EN_SN6TRISDbits.TRISD10=1;//CONT_OUT_SN6TRISDbits.TRISD11=0;//CONT_EN_SN6TRISDbits.TRISD9=0;//CURR_EN_SN6TRISDbits.TRISD10=1;//CONT_OUT_SN6TRISDbits.TRISD11=0;//CONT_EN_SN6TRISGbits.TRISG6=0;//CURR_EN_BNCTRISGbits.TRISG7=1;//CONT_OUT_BNCTRISGbits.TRISG8=0;//CONT_EN_BNCTRISEbits.TRISE6=0; // LCD_BCKLTRISDbits.TRISD1=0;//CURR_PASS_SN6TRISDbits.TRISD2=0;//CURR_PASS_SN6TRISDbits.TRISD3=0;//FAIL_SN6;TRISDbits.TRISD4=0;//CURR_PASS_BNCTRISDbits.TRISD5=0;//CURR_PASS_BNCTRISDbits.TRISD6=0;//FAIL_BNC;TRISGbits.TRISG2=0;//SN6_MODETRISGbits.TRISG3=0;//BNC_MODETRISBbits.TRISB2=1;//CURR_READ_BNCTRISBbits.TRISB3=1;//CURR_READ_SN6CONT_EN2=0;CONT_EN_SN=0;CURR_EN_SN=0;CONT_EN_BNC=0;CURR_EN_BNC=1;TEST_EN=0;}void ADCInit(){ANSBbits.ANSB2=1;ANSBbits.ANSB3=1;//Turn on, auto sample start, auto-convertAD1CON1 = 0x474; //0x0400//Vref+, Vref-, int every conversion, MUXA onlyAD1CON2 = 0x4800;AD1CON2bits.SMPI= 0b11111; //One interrupt on the 32nd sample.//31 Tad auto-sample, Tad = 5*Tcy using internal ADC RC due to sleep mode (4Mhz typ.)AD1CON3 = 0x9F05;AD1CSSL = 0;//No scanned inputs}void ADCCH(int ch){voltage=0;for (i=0; i<1024; i++){AD1CHSbits.CH0SB= ch;AD1CHSbits.CH0SA= ch;AD1CON1bits.ADON=1;IFS0bits.AD1IF=0;IEC0bits.AD1IE=1;AD1CON1bits.SAMP=1;Sleep();AD1CON1bits.ADON=0;voltage=SN_BUF+voltage;}voltage= voltage>>10;}void main() {int channel;char output;int avg;IO_Init();Lcd_Init();ADCInit();CONT_EN_SN=0;CONT_EN2=0;CURR_EN_SN=1;LCD_BCKL=1;while(1){ADCCH(SN_CH);sprintf(output,"%ld",voltage);Lcd_Command(LCD_CLEAR);Lcd_Text(1,1,output);Delay_ms(500);}}

author: dannyf
author_rank: Super Contributor
timestamp: April 15, 2016, 10:54:17 am
content: Could be a wiring issue or a Vref issue: put your meters on the Vref and see if you get good measurements there.In terms of the code, I would put the channel selection + etc. out side of the loop and just start the adc / wait for it to complete in the loop. Also try to slow down the adc to see if it helps.Check the errata if all fails.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 15, 2016, 10:56:50 am
content: Just measured the reference and its fine. Also put the channel selection outside. Whats the errata please?EDIT: Found it. No errors that relate to my issue as far as I can see.http://ww1.microchip.com/downloads/en/DeviceDoc/80470e.pdf

author: JPortici
author_rank: Super Contributor
timestamp: April 15, 2016, 10:59:31 am
content: You must always check the errata first. in some newer chips polling the go/done bit can cause reading errors, for examplethe errata is located at the bottom of the product page, in the documents sectionhttp://www.microchip.com/wwwproducts/en/PIC24FJ64GA306

author: JPortici
author_rank: Super Contributor
timestamp: April 15, 2016, 11:18:50 am
content: Quote from: dreaquil on April 15, 2016, 10:56:50 ambut you said in the opPIC24FJ64GA306the errata you are linking is for another family of chips.in this one's errata however you have the following entry:Noise injection on the A/D input pin during A/D operation (when reading a high-impedance input) may adversely affect the conversion results.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 15, 2016, 11:42:04 am
content: Shit, you're right... But it's not a high source impedance.. It's an opamp output... Anyway let me try increase the time as it says in the workaround... Can't believe I picked the micro that would fuck me over.

author: Kalvin
author_rank: Super Contributor
timestamp: April 15, 2016, 02:06:35 pm
content: Quote from: dreaquil on April 15, 2016, 10:47:51 amIn order to gain one extra bit resolution you have to make four measurements as the resolution increases at the rate of sqrt(2) when you double the number of samples.Thus, with the 1024 measurements you have gained 5 bits. To gain 10 bits, you will have to do one million measurements.

author: JPortici
author_rank: Super Contributor
timestamp: April 15, 2016, 05:37:27 pm
content: are you changing the channel by any chance? there might be "crosstalk"i had this problem in a board i'm working on. To solve it? change the channel, clear adc on bit, set adc on bit. good to go

author: dreaquil
author_rank: Regular Contributor
timestamp: April 16, 2016, 08:31:05 am
content: Quote from: Kalvin on April 15, 2016, 02:06:35 pmYou're right. Thanks for correcting me!Quote from: JPortici on April 15, 2016, 05:37:27 pm

author: Niklas
author_rank: Frequent Contributor
timestamp: April 16, 2016, 07:05:41 pm
content: Have you checked the voltage waveform on the pin of the micro? On STM32F103, where a DC signal is buffered by a 1 MHz BW opamp 20 mm away, I can clearly see when the sample and hold capacitance is charged. The opamp output was almost a flat line, but there where periodic dips that perfectly matched the sw sampling interval.A small cap, approx 1nF close to the ADC input, and a series resistance to not upset the opamp, solved our issue.

author: Lunasix
author_rank: Regular Contributor
timestamp: April 16, 2016, 10:44:52 pm
content: I was reading and was thinking exactly at the same thing. Recently, doing measurement on two different boards with an oscilloscope, I have seenwhat you described, glitches at moment of each sample. There was an op amp, dac of PIC32 on first board, and a 12 bits dac from LT on second board, but problem was exactly the same, and I also solved it adding a 1nF capacitor.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 09:37:12 am
content: After spending the whole day troubleshooting yesterday I found that the incorrect reading was coming from two places. Firstly, for some reason my precision LTC6652 reference was oscillating +- 100mV at the output of it being buffered. Luckily the LTC6652 is internally buffered (I added the extra buffer in order to have more current carrying capability) so I removed the opamp and used the reference directly. I discovered this issue because I changed the ADC reference to AVdd (3.3V) supply and it read perfectly (with 0.1mV error across the whole scale in comparison to the BM869s). I'm not sure why it was oscillating so if anyone has any thoughts on that I've attached an image of the buffered version.I tested the full scale voltage by driving one of the analog pins using my SDG810 function generator and using the DC offset while setting the amplitude to minimum (set the output to HiZ to try simulate the opamp better). When using the SDG810 it reads perfectly. The issue is when i try to read from the opamp. I think Niklas and Lunasix are correct in their suggestions. The reason I believe this is because when I probe the output of the opamp using my picoscope and multimeter, the ADC reading changes. So the added capacitance must be improving the reading? My problem now is finding a way to add this 1nF to the PCB without it looking bodged on. Would I be correct in thinking that increasing the sampling time even further would improve the situation? The opamp driving the ADC is also a low pass filter so I thought increasing the feedback capacitance to compensate for the input capacitance would improve the output stability. Thanks for your replies.

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 02:32:19 pm
content: Have you tried adding a resistor 10 ohm ... 500 ohm in series with the op amp output and placing a capacitor of 10nF ... 100nF between the resistor and ground forming a simple low-pass filter? The resistor will isolate the op amp output/feedback from the capacitor, and the capacitor will provide storage during the ADC is sampling. As the reference voltage will not change, the filtering RC will not hurt your accuracy in practice. The RC will also attenuate any residue switching noise from the LTC2057.One application note showing the technique "Capacitive Load Drive Solution using an Isolation Resistor":http://www.ti.com/lit/ug/tidu032c/tidu032c.pdfYour circuit might just work without the LTC2057 if you do not need to drive anything else than the ADC input.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 03:48:05 pm
content: Yep I removed the LTC2057 altogether and it works fine. Whats really troubling me in the measurements is that I'm getting very small oscillations on the output of my opamp. This might not be the right forum anymore but I figure ill post it here anyway. So I have the below circuit and the opamp output goes to the ADC pin directly from here (a significant distance away ~15cm).The intended operation of this circuit is to place a piece of insulation between those two SMB connectors and have the current through it be converted into a voltage (feedback ammeter). With nothing connected (so its just measuring the leakage resistance of the PCB itself I get this waveform at the output.And here it is with my oscilloscope set to AC coupling. Oscillation frequency is 180kHz and visible up to the fifth harmonic (900kHz). As well as some lower frequencies (40kHz). I can upload the FFT if you're interested.I then added a 270nF to the output without adding a resistor (cause i do not want to cut the track just yet in case there might be a way to resolve it without doing this) and this happened. I suppose here it began to oscillate due to capacitive loading. So i guess what I'm asking is whether adding the resistor and capacitor at the output will solve my issue? Before I begin cutting tracks on my PCB.

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 04:02:36 pm
content: Your circuit might be picking up some noise. The 1 Gohm resistor is quite high and the input impedance of the LMC6462 is very high too, so it makes your circuit very good radio and thus sensitive to any external noise. Do you have a switching mode power supply near the circuit? Does connecting a 100 kohm resistor to the P8 input and ground make any difference? How about 10 Mohm?

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 04:18:58 pm
content: Do you mean between P8 and P7? or just to P8 and leave the other end of the resistor in the air? If its the first one, this would saturate the opamp negatively due to the large current caused by the test voltage? I have an EMI shield surrounding my circuit as well as having a linear power supply on board with a PI filter of 3Hz to cut off any noise from the external SMPS. I think the issue might be capacitive loading but I'm not sure yet.

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 04:30:46 pm
content: Quote from: dreaquil on April 17, 2016, 04:18:58 pmBetween the P8 input and ground. If the problem goes away with 100k resistor and 10 Mohm resistor, the P8 input or the op amp input might be picking up some noise from the surrounding equipment or board's own circuits. Do you have any PCB tracks close the the P8, relay or R28 which could couple capacitically to the high impedance track?

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 04:55:54 pm
content: One other thought: The noise can couple to the high impedance track via the relay, too. How well is your 12V voltage filtered? Can you measure the ripple voltage on the 12V track feeding the relay using the AC measurement mode? Can you measure the 12V voltage and the op amp output at the same time using the AC measurement mode to see if there are any similarities?

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:03:20 pm
content: Quote from: Kalvin on April 17, 2016, 04:30:46 pmDue to the nature of the transfer function this causes the output to saturate to the supply. The only way I can do this is with a much large resistor which I currently to not have (100Gohm).

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:05:54 pm
content: Quote from: Kalvin on April 17, 2016, 04:55:54 pmYou are right! I'm not sure what I can do to solve this now

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 05:08:29 pm
content: Quote from: dreaquil on April 17, 2016, 05:03:20 pmSorry, I didn't pay attention that the P8's shield is connected to the 2.048V reference. So, connecting the 100k and 10 Mohm test resistors between the P8 input and its shield should make the output close to the zero and you should see whether the oscillating signal disappears.

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 05:12:20 pm
content: Quote from: dreaquil on April 17, 2016, 05:05:54 pmExcellent!You need to filter the 12V voltage better and/or you need to figure out how the coupling takes place. If it is layout issue and you do not want to cut tracks, filtering the 12V may help. If it is the 12V ripple coupling through the relay contact capacitance or inductive coupling to the track, you may still want to filter the 12V voltage and see if you can find a more suitable relay. At least you know now where to look at.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:15:46 pm
content: How would I go about stopping these oscillations on the 12V supply? I'm not switching the relays at all. The 24V rail also has this ripple at the same frequency

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:20:08 pm
content: I see, I'll see if I can do that. Thanks for all your help! This is my thesis and getting very close to the deadline. need this to work

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 05:22:37 pm
content: The ripple comes from the power supply connect to the CON7? Also, check that your measuring configuration is not picking up the noise ie. you are not having some grounding issues which would cause the noise ripple seen on the oscilloscope.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:32:47 pm
content: Yep it's coming through CON7 from the external SMPS 36V. I put an inductor/capacitor filter on the input with Fc=36Hz for this reason :/

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 05:41:28 pm
content: Just powered the board using my regulated bench top supply and it has the exact same oscillations. So it must be created on the board. What I find strange is that the 3.3V is clean as a whistle but the 24V and 12V are not.

author: Niklas
author_rank: Frequent Contributor
timestamp: April 17, 2016, 07:57:36 pm
content: Quote from: dreaquil on April 17, 2016, 05:41:28 pmAll linear regulators are not created equal. A lot of the older ones, often named 78*XX, usually attenuates 50/60/100/120 Hz ripple but higher frequencies will pass through. LC-filters before and in between the regulators might help. Another thing you can try is to swap the ceramic capacitors on the outputs of the 78** to something with a little bit more ESR, for instance tantalum or ceramic cap with a 1ohm series resistor. I found no info the datasheet for the 78** from OnSemi, but it might be sensitive to too low ESR on the output.You also asked about increasing the sampling time in a previous post. Give it a try and see if it gives any improvement. If you have an oscilloscope, try to set the sampling time longer than the dip on the ADC input.

author: Kalvin
author_rank: Super Contributor
timestamp: April 17, 2016, 08:02:17 pm
content: Quote from: Niklas on April 17, 2016, 07:57:36 pmYour suggestion reminds me of another trick which might be useful too. If the MCU can be synchronized to the noise, the MCU can trigger the ADC measurement between the noise spikes effectively cancelling the noise completely.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 17, 2016, 10:57:13 pm
content: I'll be trying these things tomorrow if I find the right parts. There is one thing that's puzzling me though- it's where the oscillations are coming from... if theyre not coming from the switch mode. I know how theyre coupling onto the output of the opamp because the supply of the opamp has the exact same oscillations ... so where are they being created.

author: dannyf
author_rank: Super Contributor
timestamp: April 18, 2016, 01:25:40 am
content: QuoteHigh performance parts + poor layout are usually to blame. High speed opamps are especially evil and should be avoided as much as possible. My philosophy is to use as slow of a part as possible.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 18, 2016, 06:40:33 am
content: That's the thing though... The parts I used are pretty slow. I don't know about my layout but I did give it a lot of thought... Clearly not enough

author: dannyf
author_rank: Super Contributor
timestamp: April 18, 2016, 10:49:09 am
content: Then maybe the opamp is driving a capacitive load, is in too much feedback, or too high impedance, .....

author: dreaquil
author_rank: Regular Contributor
timestamp: April 18, 2016, 05:59:41 pm
content: Thanks to everyone for the help. Found the issue, had an opamp with a large capacitive load which was amplifying my reference by 2. This was in turn supplied by a 12V supply. It was going unstable and causing the supply rails to go unstable with it. Really appreciate all your suggestions- wouldve taken me much longer than it did to solve it.

author: dannyf
author_rank: Super Contributor
timestamp: April 18, 2016, 10:23:58 pm
content: In those cases, out an isolation resistor before the capacitor and pick up the feedback on the capacitor but after the isolation resistor.It prevents oscillation.

author: dreaquil
author_rank: Regular Contributor
timestamp: April 19, 2016, 05:34:44 am
content: Yeah, will have to keep it in mind for next time but wasn't even aware of the possibility while designing this time. I guess its all experience and knowing what to look for.