Electrical – How to read a specific port with ADC in Mplab Harmony

microchipmicrocontrollermplab-harmonymplabxpic

I configured the ADC for 3 instances/3 analog inputs:

enter image description here
enter image description here

In APP_STATE_INIT i enable and start ADC:

    case APP_STATE_INIT:
    {
        bool appInitialized = true;


        if (appInitialized)
        {

            appData.state = APP_STATE_SERVICE_TASKS;
        }
        DRV_TMR0_Start(); // Start the Timers
        DRV_ADC_Initiallize();
        DRV_ADC_Enable();
    }

Now in this test application I want to read the ports.
But in this specific case I want to read the Channel Instance 0 and Instance 1 (each instance will have changing values that can be configured with a dil-switch):

    case APP_STATE_SERVICE_TASKS:
    {
        DRV_ADC_Start();
        //Provide Delay
        int i;
        for(i=0;i <1000;i++)
        {
        }
        DRV_ADC_Stop();
        while(!DRV_ADC_SamplesAvailable()) {}
        printf("addr: %d \r\n",DRV_ADC_SamplesRead(i)>>2);
        break;
    }

This is alternating between values when it's not supposed. I configured 3 channels so I suppose it's alternating between them, how to choose a specific channel?

What needs to be done here?

The pins are configured this way:

enter image description here
enter image description here

Basically what i would like is something like we have in the pic32 family reference manual in
Example 17-1: Converting 1 Channel, Manual Sample Start, Manual Conversion Start Code

 AD1PCFG = 0xFFFB; // PORTB = Digital; RB2 = analog
 AD1CON1 = 0x0000; // SAMP bit = 0 ends sampling ...
 // and starts converting
 AD1CHS = 0x00020000; // Connect RB2/AN2 as CH0 input ..
 // in this example RB2/AN2 is the input
 AD1CSSL = 0;
 AD1CON3 = 0x0002; // Manual Sample, Tad = internal 6 TPB
 AD1CON2 = 0;
 AD1CON1SET = 0x8000; // turn ADC ON
 while (1) // repeat continuously
 {
     AD1CON1SET = 0x0002;         // start sampling ...
     DelayNmSec(100);             // for 100 mS
     AD1CON1CLR = 0x0002;         // start Converting
     while (!(AD1CON1 & 0x0001)); // conversion done?
     ADCValue = ADC1BUF0;         // yes then get ADC value
 }

Where we can choose between channel 14,15 and 22.

Note: It seems to work well if i only have 1 instance running.
This probably has something to do with the multiplex which i dont fully understand.

Using:

  • MPLAB Harmony v2
  • MPLAB X IDE v5.20
  • PIC32MX470F512L configured to run at 32MHz

EDIT:
After applying @pm101 answer it still keeps reading what is not supposed.
enter image description here

As we can see on termite:
enter image description here

Doing the other solution:

int conversion;
uint8_t j = 0;
while(j++<3) {
    DRV_ADC_Start();        
    //Provide Delay
        int i;
        for(i=0;i <1000;i++)
        {
        }    
    while(!DRV_ADC_SamplesAvailable()) {}
}
DRV_ADC_Stop();
conversion = DRV_ADC_SamplesRead(val)>>2;//Val will be the sample we want to read.
return (unsigned char)conversion;

The value is always the same?

TEST 19/06

I did the following to my ADC_GetConversion function:

unsigned char ADC_GetConversion(uint8_t val){ 
    int conversion;
    switch(val) {
        case 0:
            AD1CSSLCLR = 0x00000000;
            AD1CSSLSET = 0x00004000;//AN14
            break;            
        case 1:
            AD1CSSLCLR = 0x00000000;
            AD1CSSLSET = 0x00008000;//AN15
            break;
    }
    DRV_ADC_Start();        
        //Provide Delay
        int i;
        for(i=0;i <1000;i++)
        {
        }    
       while(!DRV_ADC_SamplesAvailable()) {}
    DRV_ADC_Stop();
conversion = DRV_ADC_SamplesRead(0)>>2;
return (unsigned char)conversion;
}

then on a while cycle i do:

printf("addr%d: %d \r\n",14,decode_switch_conversion(ADC_GetConversion(0)));
printf("addr%d: %d \r\n",15,decode_switch_conversion(ADC_GetConversion(1)));

Context: The decode_switch_conversion converts these adc tension conversion to the correspondent value that was coded in a four switch (only switch number 1 down means we want the value 1) . AN14 there is connected a 4 switch, and the AN15 there is another 4 four switch.

But those 2 prints get the value switched for both?
The result i get from termite:
enter image description here

In reality i have 1 for the switch of AN14 and 3 for the switch of AN15.

But if i only read 1 channel and never do the lines AD1CSSLCLR and AD1CSSLSET the values are good!

Best Answer

Honestly, I think the Harmony examples for ADC are poor.

Harmony automatically adds your channels to the scan list in DRV_ADC_Initialize with the function PLIB_ADC_InputScanMaskAdd so you shouldn't need to do that again.

I think what you are looking for is actually what you are using (DRV_ADC_SamplesRead) but not using it correctly.

If you look at the harmony source code for this function , the prototype is ADC_SAMPLE DRV_ADC_SamplesRead(uint8_t bufIndex) . You might also notice that this is just a wrapper function for PLIB_ADC_ResultGetByIndex see Harmony Libraries guide page 55-56.

It requires a buffer index as a parameter. The buffer index however is not the same as the channel index. see DS61104E-page 40

The buffer index will probably be the following as they are in order from ANxx and in your scan mask set up by Harmony.

0 AN14 
1 AN15 
2 AN22

You will need to take three readings. You will need to call DRV_ADC_Start before each scan to overwrite the buffer. You will also need to enable the ADC before starting using DRV_ADC_Open

Also I wouldn't stop the ADC until DRV_ADC_SamplesAvailable() returns non zero.

So the steps are

  1. DRV_ADC_Open()
  2. DRV_ADC_Start()
  3. wait until DRV_ADC_SamplesAvailable() is true . Go back to 2 until 3 channels read
  4. DRV_ADC_Stop()
  5. AN14 = DRV_ADC_SamplesRead(0)
  6. AN15 = DRV_ADC_SamplesRead(1)
  7. AN22 = DRV_ADC_SamplesRead(2)

You may either implement as a state machine or use while loop to wait on samples available.

But this isn't a proper scan. To do a scan, you need to set ASAM which (Harmony functions don't appear to do?) There is a PLIB function, but I'd just do the following

First Set :

AD1CON2bits.SMPI = 2

This will set the interrupt flag high when scan has converted three readings I made a function below:

void ADC_StartScan ( unsigned char numChannels )
{
   IFS0bits.ADIF = 0; // reset interrupt flag
   AD1CON2bits.SMPI = numChannels-1; // set number of channels to sample before interrupt flag goes high
   AD1CON1bits.ASAM = 1; // start scan
   while (!IFS0bits.ADIF); // wait for interrupt flag
   AD1CON1bits.ASAM = 0; // stop scan
}

(see page 52 of DS61104E)

Usage

ADC_StartScan(3);

AN14 = DRV_ADC_SamplesRead(0);
AN15 = DRV_ADC_SamplesRead(1);
AN22 = DRV_ADC_SamplesRead(2);

Disclaimer: Pure research and no coding done

Personally I'd use dedicated channel as it's probably easier to work with, Some example code I found with nice state machine here

Related Topic