// © RiceMotion ( Robert Carl Rice ) 2012-2016 - All rights reserved


// This software makes use of tools and libraries obtained from open source projects or released for

// use by relevant hardware manufactures. However, this software is NOT a part of any open source project.

// It is released only on a "need to know" basis for beta testers of the "RiceCNC Interpolation Engine".

// Recipents of this source code must respect the confidential nature of this software and prevent any

// distribution that could result in counterfeit copies of the "RiceCNC Interpolation Engine".


// © RiceMotion ( Robert Carl Rice ) 2012-2016 - All rights reserved

#include <ADC.h>

#include <AnalogPin.h>


#include "inc/hw_adc.h"


// Allocate and initialize static variables

bool          ADC::adcRunning               = false;

AnalogPin*     ADC::sequencerReg[8][8]     = { NULL };


void

     adc0Seq0InterruptHandler(void) {


     ADC::intHandler(

          ADC0_BASE,

          0,

          ADC0_BASE + ADC_O_SSFIFO0 ); };


void

     adc0Seq1InterruptHandler(void) {


     ADC::intHandler(

          ADC0_BASE,

          1,

          ADC0_BASE + ADC_O_SSFIFO1 ); };


void

     adc0Seq2InterruptHandler(void) {


     ADC::intHandler(

          ADC0_BASE,

          2,

          ADC0_BASE + ADC_O_SSFIFO2 ); };


void

     adc0Seq3InterruptHandler(void) {


     ADC::intHandler(

          ADC0_BASE,

          3,

          ADC0_BASE + ADC_O_SSFIFO3 ); };



void

     adc1Seq0InterruptHandler(void) {


     ADC::intHandler(

          ADC1_BASE,

          4,

          ADC1_BASE + ADC_O_SSFIFO0 ); };


void

     adc1Seq1InterruptHandler(void) {


     ADC::intHandler(

          ADC1_BASE,

          5,

          ADC1_BASE + ADC_O_SSFIFO1 ); };


void

     adc1Seq2InterruptHandler(void) {


     ADC::intHandler(

          ADC1_BASE,

          6,

          ADC1_BASE + ADC_O_SSFIFO2 ); };


void

     adc1Seq3InterruptHandler(void) {


     ADC::intHandler(

          ADC1_BASE,

          7,

          ADC1_BASE + ADC_O_SSFIFO3 ); };


void

     ADC::configure() {


     for ( int i = 0; i < 8; i++ )

          for ( int j = 0; j < 8; j++ )

               sequencerReg[ i ][ j ]     = NULL;


    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC0 );

    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC1 );

     

     configureBase( ADC0_BASE );

     configureBase( ADC1_BASE );


     ADCIntRegister(

          ADC0_BASE,

          0,

          adc0Seq0InterruptHandler );


     ADCIntRegister(

          ADC0_BASE,

          1,

          adc0Seq1InterruptHandler );


     ADCIntRegister(

          ADC0_BASE,

          2,

          adc0Seq2InterruptHandler );


     ADCIntRegister(

          ADC0_BASE,

          3,

          adc0Seq3InterruptHandler );


     ADCIntRegister(

          ADC1_BASE,

          0,

          adc1Seq0InterruptHandler );


     ADCIntRegister(

          ADC1_BASE,

          1,

          adc1Seq1InterruptHandler );


     ADCIntRegister(

          ADC1_BASE,

          2,

          adc1Seq2InterruptHandler );


     ADCIntRegister(

          ADC1_BASE,

          3,

          adc1Seq3InterruptHandler );


    // clear interrupts

    HWREG( ADC0_BASE + ADC_O_ISC )     = 0xF;

     };


void

     ADC::configureBase( uint32_t adcBase ) {


     // Disable all sequences

     HWREG( adcBase + ADC_O_ACTSS )     = 0;


     // Set all sequencers to default ADC_TRIGGER_PROCESSOR

     HWREG( adcBase + ADC_O_EMUX )     = 0;


     // Set hardware oversampling

     HWREG( adcBase + ADC_O_SAC )     = 6;


    // Set the priority for sample sequences.

     HWREG( adcBase + ADC_O_SSPRI )     = 3;

     

     ADCReferenceSet(

          adcBase,

          ADC_REF_INT);

     

     // Enable interrupts

     HWREG( adcBase + ADC_O_IM )          = 0xF; };


void

     ADC::addAnalogPin( AnalogPin* analogPin ) {

     

     for (     uint32_t sequenceNumber = 0;

               sequenceNumber < 8;

               sequenceNumber++ ) {


          uint32_t adcSeq          = sequenceNumber & 3;


          uint32_t depth     = 8;

          if ( adcSeq )

               depth     = 4;

          if ( adcSeq == 3 )

               depth     = 1;


          for (     uint32_t step = 0;

                    step < depth;

                    step++ ) {


               if ( sequencerReg[ sequenceNumber ][ step ] == NULL ) {

                    sequencerReg[ sequenceNumber ][ step ] = analogPin;


                    uint32_t adcBase     = sequenceNumber < 4 ?

                         ADC0_BASE

                    :     ADC1_BASE;


                    uint32_t seqOffset     =

                         adcBase +

                         ( ADC_O_SSMUX1 - ADC_O_SSMUX0 ) * adcSeq;


                    uint32_t nibble          = step << 2;     // * 4


                    // Disable the sequencer

                    HWREG( adcBase     + ADC_O_ACTSS )          &= ~( 1 << adcSeq );


                    // Set the analog mux value for this step.

                    HWREG( seqOffset + ADC_O_SSMUX0 )     &= ~( 0xF << nibble );

                    HWREG( seqOffset + ADC_O_SSMUX0 )     |=

                         ( analogPin->aid->channelSelect & 0xF ) << nibble;


                    // Set the analog mux extended bit for this step.

                    HWREG( seqOffset + ADC_O_SSEMUX0 )     &= ~( 0xF << nibble );

                    HWREG( seqOffset + ADC_O_SSEMUX0 )     |=

                         ( analogPin->aid->channelSelect >> 8 ) << nibble;


                    // Set the last nibble to IE, END, single end, not temp

                    HWREG( seqOffset + ADC_O_SSCTL0 )     = 0x6 << nibble;


                    // Disable comparator output

                    HWREG( seqOffset + ADC_O_SSOP0 )     &= ~( 1 << nibble );


                    // Enable the sequencer

                    HWREG( adcBase     + ADC_O_ACTSS )          |= 1 << adcSeq;


                    if ( ! adcRunning )

                         triggerNextSequence(

                              adcBase,

                              sequenceNumber );

                    

                    return; }; }; }; };


void

     ADC::removeAnalogPin( AnalogPin* analogPin ) {


     for (     uint32_t sequenceNumber = 0;

               sequenceNumber < 8;

               sequenceNumber++ ) {


          for (     uint32_t step = 0;

                    step < 8;

                    step++ ) {


               if ( sequencerReg[ sequenceNumber ][ step ] == analogPin ) {


                    while ( step < 8 ) {

                         sequencerReg[ sequenceNumber ][ step ] = step == 7 ?

                              NULL

                         :     sequencerReg[ sequenceNumber ][ step + 1 ];

                         step++; };


                    uint32_t adcBase     = sequenceNumber < 4 ?

                         ADC0_BASE

                    :     ADC1_BASE;


                    //     Safely stop the ADC converter

                    // Set all sequencers to never trigger

                    HWREG( adcBase + ADC_O_EMUX )     = 0xEEEE;

                    // Wait until not busy

                    while ( HWREG( adcBase + ADC_O_ACTSS ) & ADC_ACTSS_BUSY );


                    uint32_t adcSeq          = sequenceNumber & 3;


                    uint32_t seqOffset     =

                         adcBase +

                         ( ADC_O_SSMUX1 - ADC_O_SSMUX0 ) * adcSeq;


                    uint32_t nibble          = step << 2;

                    uint32_t mask          = 0xFFFFFFFF << nibble;


                    // Disable the sequencer

                    HWREG( adcBase     + ADC_O_ACTSS )          &= ~( 1 << adcSeq );


                    // shift the nibble out of the registers

                    uint32_t cValue          = HWREG( seqOffset + ADC_O_SSMUX0 );

                    HWREG( seqOffset + ADC_O_SSMUX0 )     &= ~mask;

                    HWREG( seqOffset + ADC_O_SSMUX0 )     |= ( cValue >> 4 ) & mask;


                    cValue                    = HWREG( seqOffset + ADC_O_SSEMUX0 );

                    HWREG( seqOffset + ADC_O_SSEMUX0 )     &= ~mask;

                    HWREG( seqOffset + ADC_O_SSEMUX0 )     |= ( cValue >> 4 ) & mask;


                    // Set the control for all steps - only one end interrupt

                    HWREG( seqOffset + ADC_O_SSCTL0 )     >>= 4;

                    if ( HWREG( seqOffset + ADC_O_SSCTL0 ) )

                         // Reenable the sequencer

                         HWREG( adcBase     + ADC_O_ACTSS )          |= 1 << adcSeq;


                    // Set all sequencers to default ADC_TRIGGER_PROCESSOR

                    HWREG( adcBase + ADC_O_EMUX )     = 0;


                    triggerNextSequence(

                         adcBase,

                         sequenceNumber );


                    return; }; }; }; };


void

     ADC::intHandler(

          uint32_t     adcBase,

          uint32_t     sequenceNumber,

          uint32_t     fifo ) {

     

     uint32_t          adcSeq     = sequenceNumber & 3;


     //     Clear the interrupt

     HWREG( adcBase + ADC_O_ISC )     = 1 << adcSeq;


     for (     uint32_t step = 0;

               step < 8;

               step++ ) {

          AnalogPin* pin     = sequencerReg[ sequenceNumber ][ step ];

          if ( ! pin )

               break;

          pin->averageSample( HWREG( fifo ) ); };

     

     HWREG( fifo ); // extraneous read ensures synchronization


     // Loop by trigerring the next non-empty sequence

     triggerNextSequence(

          adcBase,

          sequenceNumber ); };


void

     ADC::triggerNextSequence(

          uint32_t     adcBase,

          uint32_t     sequenceNumber ) {


     for (     uint32_t i = 0;

               i < 8;

               i++ ) {

          sequenceNumber++;

          sequenceNumber     &= 7;

          if ( sequencerReg[ sequenceNumber ][ 0 ] ) {

               adcBase          = sequenceNumber < 4 ?

                    ADC0_BASE :

                    ADC1_BASE;

               ADCProcessorTrigger(

                    adcBase,

                    sequenceNumber & 3 );

               adcRunning          = true;

               return; }; };


     adcRunning          = false; };