18F26K40 single ADC to read RMS voltage and slow rate DC voltage on different pins
The background is that I have built a pump controller which currently monitors water flow using 5V pulses from a sensor, and pressure using analog DC from a sensor, and I now want to add pump current draw to the display.
I looked at the MCP39F511A, which does all I need and more and presents them out a serial port by request, but I am using the onboard UART for the display and writing a full serial protocol in code is a little too much work for the present. Maybe next design I will use the 40 pin version which has two UART's onboard. I can easily add an ACS770 to the PCB, which gives me an analog output representing the current flow through it.
I need to get an idea of how to re-write my code to read two differing ADC inputs. One varies very little over a second (pressure, which I sample and display twice per second), the other will be sampling a 50Hz sine wave to determine RMS current which will obviously need many more samples.
Current to voltage and high voltage isolation is taken care of by ACS770 sensor which outputs analog 40mV per amp with zero being at half (5V) rail. At 10 amps for example, sine wave input to ADC pin would be varying between 2.1V and 2.9V.
My primary question is what is the best way to re-write to get enough samples to calculate the RMS value while still sampling my pressure ADC.
The ADC is currently set to FRC, free-running at 600KHz and I just start a conversion each sample time.
My initialisation routine includes:
ANSELA = 0b00000001; //RA0 analog input
// Change to ANSELA = 0b00000011; //RA0 and RA1 analog inputs
ANSELB = 0x00;
ANSELC = 0x00;
//10 bit resolution, result in ADCRESH and ADRESL, Right justified.
ADON = 0; //Ensure ADC is off
ADCON0bits.ADFM = 1; //Right justify result into ADRESH and ADRESL
OSCENbits.ADOEN = 1;
ADCON0bits.ADCS = 1; //Select FRC Clock 600KHz free-running internal osc
while (!OSCSTATbits.ADOR); //Wait for AD oscillator to be ready
ADPCH = 0x00; //RA0 is Analog channel
ADCON0bits.ADON = 1; //Turn ADC On
//TMR0 8 bit, Prescaler 1:1, Preload TMR0H 20 = 1mS
T0CON0bits.T0EN = 0; //Stop timer0 while we setup parameters
PIE0bits.TMR0IE = 1; //Enable Timer0 Interrupts
T0CON1bits.T0CS = 5; //2 FOSC/4 as source (16Mhz))
T0CON0bits.T016BIT = 0; //Set timer0 as 8 bit
T0CON1bits.T0CKPS = 0; //Prescaler 1:1
T0CON1bits.T0ASYNC = 1; //Async mode - Must be set - bug from errata
TMR0H = 0x20; //Preload 20 into TMR0H counter to make 1mS
TMR0L = 0x00;
PIR0bits.TMR0IF = 0; //Clear Interrupt flag
T0CON0bits.T0EN = 1; //Enable timer 0
Every 500mS I execute this:
//Read ADC pressure sensor pin
ADCON0bits.GO = 1; //Start conversion
GO_nDONE = 1;
while (ADCON0bits.GO) continue; //Wait for conversion done
ADCRead = (ADRESH <<8) + ADRESL; //Concatenate ADC high and ADC low bytes
SamplePSI = ((ADCRead - 100)/8.1); //Sensor 0 pressure voltage offset
LastPSI = SamplePSI;
if (LastPSI > 100) LastPSI = 0; //Sanity check - can't rollover if less than 0.
Accuracy is not critical, 5% on the displayed current value (0.1 amps resolution) would be fine and displayed 4 times per second would be ideal. From reading around, I have found varying requirements from 4 samples per cycle being adequate to many more required.
Should I, for example, change the ADC to run every millisecond?
Is it still ok to have it running from FRC?
My mind tells me that I should sample RA1 every millisecond, take 10 samples and look for the peak, then do the calculation to determine RMS (or just lookup in a table for efficiency). How then, do I sample the pressure in between without dropping too many samples. If I just Read_Pressure at 1mS intervals will there be any issues with the sampling (capacitor charging etc.).
I also see that the processor has a ZCD module. Should I sense the zero cross, then just sample at 5mS from that point (or maybe at 4mS, 5mS, 6mS and average) as it will always be a sine wave at 50Hz.
I'd appreciate any insights or thoughts or even better somebody who has actually done it before.