# How to read a specific port with ADC in Mplab Harmony?

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

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:

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.

As we can see on termite:

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:

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!

• You have asked a ton of questions very closely related, which is fine. But, is there no company provided training to get to up to speed with working with these tools? – Tyler Jun 15 '19 at 10:22
• Hi @Tyler, on my company which have a small R&D team there is no one that works with these framework, anyhow I have not found on the internet a satisfactory answer to this problem, so I posted the question here in hope it can help me and anyone who searches for the same. – Nmaster88 Jun 15 '19 at 17:40
• Anyway @Tyler what i've did seems to work to a certain point, i just need to find how i can do a new scan on a pin. – Nmaster88 Jun 17 '19 at 11:49
• @Nmaster88 Harmony comes installed with a load of example projects. You should look at them – Oscillonoscope Jun 17 '19 at 15:45
• Posting 2-3 of these questions each day after day isn't really how this site is supposed to be used; you need to get better about reading manuals, doing your own research and consulting examples - this is not a custom tutorial generation service. This has already been pointed out to you, so if you're going to keep doing it, so you'll need to get used to being downvoted (the hover text on the downvote button specifically mentions insufficient research effort) – Chris Stratton Jun 17 '19 at 19:19

## 3 Answers

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

• Hi @pm101 thanks for answer, in DRV_ADC_SamplesRead(0) if i read the same number it will alternate between the values on the different channels. – Nmaster88 Jun 18 '19 at 17:00
• It could be that you'll need to do steps 2 and 3, 3 times, see page 41 of DS61104E. When SAMP is high, the next in the scan is chosen. DRV_ADC_Start only sets SAMP high, not ASAM. Because you're in poll mode this might be the only way. Typically scanning is done in interrupts. – pm101 Jun 18 '19 at 17:46
• I've updated the answer to provide some polling scan code. I agree that harmony's ADC implementation is rather poorly documented for beginners. – pm101 Jun 18 '19 at 18:09
• That seems nice, but still it's taking the wrong reading? if i'm in a while always reading DRV_ADC_SamplesRead(0). Thank you so much. – Nmaster88 Jun 19 '19 at 8:15
• If you configure only one sample per interrupt it will only read the first channel each time. – pm101 Jun 22 '19 at 15:01

You had to trigger each sample on its own. Here is a really good explanation of the complete Harmony ADC API here: ADC peripheral library interface with examples.

I guess you are looking for a function like:

int readADC(intchannel)


You had to write such a function on your own, but it should be really easy with the example from the datasheet.

• these seems the function i want DRV_ADC_InputsRegister: This function causes the driver to select a set of input registers for sampling. But it's not in my drv_adc_static.c i have there DRV_ADC_ChannelScanInputsAdd and DRV_ADC_ChannelScanInputsRemove. – Nmaster88 Jun 18 '19 at 13:05

For now i implemented ADC the same way that MCC configures it. So in SYS_Initialize i have:

/*Initialize ADC */
// ASAM disabled; DONE disabled; CLRASAM disabled; FORM Integer 16 bit; SAMP disabled; SSRC Clearing sample bit ends sampling and starts conversion; SIDL disabled; ON enabled;
AD1CON1 = 0x8000;

// CSCNA disabled; ALTS disabled; BUFM disabled; SMPI 1; OFFCAL disabled; VCFG AVDD/AVSS;
AD1CON2 = 0x0;

// SAMC 0; ADRC PBCLK; ADCS 0;
AD1CON3 = 0x0;

// CH0SA AN14; CH0SB AN15; CH0NB Vrefl; CH0NA Vrefl;
AD1CHS = 0xF0E0000;

// CSSL26 disabled; CSSL25 disabled; CSSL28 disabled; CSSL27 disabled; CSSL22 disabled; CSSL21 disabled; CSSL24 disabled; CSSL9 disabled; CSSL23 disabled; CSSL20 disabled; CSSL0 disabled; CSSL8 disabled; CSSL7 disabled; CSSL6 disabled; CSSL5 disabled; CSSL4 disabled; CSSL3 disabled; CSSL29 disabled; CSSL2 disabled; CSSL1 disabled; CSSL15 disabled; CSSL14 disabled; CSSL17 disabled; CSSL16 disabled; CSSL11 disabled; CSSL10 disabled; CSSL13 disabled; CSSL12 disabled; CSSL30 disabled; CSSL19 disabled; CSSL18 disabled;
AD1CSSL = 0x0;


Then in my ADC_GetConversion function:

unsigned char ADC_GetConversion(uint8_t val){
int conversion;
uint32_t channel;
switch(val) {
case 0:
channel = 0xE; // ADR1 AN14
break;
case 1:
channel = 0xF; // ADR2 AN15
break;
case 2:
channel = 0x16; //DEVICE RESPONSE AN22
break;
}
AD1CHS = channel << 16;
AD1CON1bits.SAMP = 1;
//Provide Delay
int i;
for(i=0;i <1000;i++)
{
}
AD1CON1bits.SAMP = 0;
while(!AD1CON1bits.DONE) {
if(IFS0bits.AD1IF)
{
// Read ADC Buffer since the interrupt is persistent

// clear ADC interrupt flag
IFS0CLR= 1 << _IFS0_AD1IF_POSITION;
}
}

conversion = ADC1BUF0 >> 2;
return (unsigned char)conversion;
}


At least for now this will do.

I can't understand how ADC is so hard to configure with Harmony, it seems a lot easier to simply go to the datasheet i copy the base code.

• As long as this works, you can integrate it with your harmony code no problem. Just be careful of too much blocking code, you could implement a state machine. I think the fact it has been set up for single channel rather than scan mode here makes it easier. With your 'scan' and 'poll' set up it is done differently and the harmony functions don't seem to be easily understandable for getting scans to work in poll mode. – pm101 Jun 26 '19 at 7:46