/****************************************************************************/
/* Copyright 2016 MBARI                                                     */
/****************************************************************************/
/* Summary  : A/D Converter Routines for OASIS5 on PIC32MX470F512L          */
/* Filename : adc.c                                                         */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 02/23/2016                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 23feb2016 rah - created                                                  */
/****************************************************************************/
/* Using this module:                                                       */
/* 1. Call adcSetup{Single,Mult}                                            */
/* 2. Wait 10 ms for ADC power to stabilize                                 */
/* 3. Repeat as necessary:  call adcConvert{Single,Mult}                    */
/* 3b. Delay as needed, go to 3                                             */
/* 4. Call adcStop()                                                        */
/****************************************************************************/

#include <xc.h>
#include <GenericTypeDefs.h>

#include "adc.h"
#include "dig_io.h"
#include "sys_timer.h"

volatile unsigned int *adcResult[] =
    {&ADC1BUF0, &ADC1BUF1, &ADC1BUF2, &ADC1BUF3,
     &ADC1BUF4, &ADC1BUF5, &ADC1BUF6, &ADC1BUF7,
     &ADC1BUF8, &ADC1BUF9, &ADC1BUFA, &ADC1BUFB,
     &ADC1BUFC, &ADC1BUFD, &ADC1BUFE, &ADC1BUFF
    };


/************************************************************************/
/* Function    : adcSetupSingle                                         */
/* Purpose     : Setup ADC module to convert one channel                */
/* Inputs      : ADC Channel                                            */
/* Outputs     : None                                                   */
/************************************************************************/
void adcSetupSingle(int chan)
{
    digEnvEnable();
    AD1CON1 = 0x0005;
    AD1CON2 = 0x0000;
    AD1CON3 = 0x0000;
    AD1CON3bits.SAMC = 12;
    AD1CON3bits.ADCS = 12;
    AD1CHS = ((chan & 0x1f) << 16);
    AD1CSSL = 0;
    AD1CON1SET = _AD1CON1_ON_MASK;
}


/************************************************************************/
/* Function    : adcSetupMult                                           */
/* Purpose     : Setup ADC module to convert multiple channels          */
/* Inputs      : First ADC Channel, number of channels                  */
/* Outputs     : None                                                   */
/************************************************************************/
void adcSetupMult(int chan, int nchans)
{
    digEnvEnable();
    AD1CON1 = 0x00e5;
    AD1CON2 = 0;
    AD1CON2bits.CSCNA = 1;
    AD1CON2bits.SMPI = nchans - 1;
    AD1CON3 = 0;
    AD1CON3bits.SAMC = 12;
    AD1CON3bits.ADCS = 12;
    AD1CHS = ((chan & 0x1f) << 16);
    AD1CSSL = ((1 << nchans) - 1) << chan;
    AD1CON1SET = _AD1CON1_ON_MASK;
}


/************************************************************************/
/* Function    : adcStop                                                */
/* Purpose     : Inverse of adcSetupxxx, stop using ADC                 */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void adcStop(void)
{
    AD1CON1CLR = _AD1CON1_ON_MASK;
    digEnvDisable();
}


/************************************************************************/
/* Function    : adcInit                                                */
/* Purpose     : Initialize this module                                 */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void adcInit()
{
    AD1CON1 = 0x0010;
    AD1CON2 = 0x0000;
    AD1CON3 = 0x0000;
    AD1CON3bits.SAMC = 12;
    AD1CON3bits.ADCS = 12;
}


/************************************************************************/
/* Function    : adcConvertSingle                                       */
/* Purpose     : Convert one A/D channel                                */
/* Inputs      : None                                                   */
/* Outputs     : A/D value, 0-1023                                      */
/* Comments    : Must first call adcSetupSingle()                       */
/************************************************************************/
UINT16 adcConvertSingle(void)
{
    AD1CON1CLR = _AD1CON1_SAMP_MASK;

    while(!AD1CON1bits.DONE)
        ;
    return(ADC1BUF0);
}


/************************************************************************/
/* Function    : adcConvertMult                                         */
/* Purpose     : Convert one A/D channel                                */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/* Comments    : Must first call adcSetupMult()                         */
/*               Results are in ADC1BUF0 - ADC1BUFn                     */
/************************************************************************/
void adcConvertMult(void)
{
    IFS0CLR = _IFS0_AD1IF_MASK;
    AD1CON1CLR = _AD1CON1_SAMP_MASK;

    while(!IFS0bits.AD1IF)
        ;
}

/************************************************************************/
/* Function    : adcGetResult                                           */
/* Purpose     : Get Result of adcConvertMult for one channel           */
/* Inputs      : Offset from first channel converted (chan parm to adcSetupMult())*/
/* Outputs     : Result                                                 */
/************************************************************************/
UINT16 adcGetResult(UINT16 chanOfs)
{
    return(*adcResult[chanOfs]);
}
