ADC conversion with ASF on SAM D10 Xplained board doesnt work

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

i am a newbie in programming an ARM controller with ASF.

 

I use the SAM D10 Xplained board with Atmel Studio 6.2 and i want to make an ADC conversion like the example in "Quick Start Guide for ADC - Basic"

http://asf.atmel.com/docs/3.16.0/samd21/html/asfdoc_sam0_adc_basic_use_case.html

 

 

In this use case, the ADC will be configured with the following settings:

  • 1V from internal bandgap reference
  • Div 4 clock prescaler
  • 12 bit resolution
  • Window monitor disabled
  • No gain
  • Positive input on ADC PIN 0
  • Negative input on ADC PIN 1
  • Averaging disabled
  • Oversampling disabled
  • Right adjust data
  • Single-ended mode
  • Free running disabled
  • All events (input and generation) disabled
  • Sleep operation disabled
  • No reference compensation
  • No gain/offset correction
  • No added sampling time
  • Pin scan mode disabled

 

and so i understood correctly, the function...

adc_get_config_defaults(&config_adc);		// Initialize the ADC configuration struct with the module's default values.

...initialize the ADC input @ pin 1 (AIN0 @ PA2) ?

 

It seems that the code works fine, but i get a value of 4095 from the variable adc_result. The measured voltage over a voltage divider @ the ADC input pin PA2 is 0,1V.

 

Hope someone can give me a hint what´s wrong with my code.

 

 

Here is my code:

#include <asf.h>

struct adc_module adc_instance;
void configure_adc(void);

void configure_adc(void)
{
	struct adc_config config_adc;				// Create a ADC module configuration struct, which can be filled out to adjust the configuration of a physical ADC peripheral.
	
	adc_get_config_defaults(&config_adc);		// Initialize the ADC configuration struct with the module's default values.
												// This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
	adc_init(&adc_instance, ADC, &config_adc);	// Set ADC configurations
	adc_enable(&adc_instance);					// Enable the ADC module so that conversions can be made.
}

uint16_t adc_result;							// global variable

int main (void)
{
	system_init();		
	configure_adc();
	
	adc_start_conversion(&adc_instance);		// start conversion
	
	uint16_t result;
	
	do 
	{
		/* Wait for conversion to be done and read out result */
	} while (adc_read(&adc_instance, &result) == STATUS_BUSY);
	
	adc_result = result;
	    
	while (1) 
	{
		asm ("NOP");
	}
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't use ASF however if you would like to compare this code example as it performs a differential ADC reading on Pins PA02 and PA03 without issue on the SAM D10 Xplained board with Atmel Studio 6.2.

 

volatile int16_t myResult;

/*******************************************************************************
Object: Change the default CPU clock divider so the CPU frequency changes
from 1MHz to 8MHz
Parameters: none
Return: Nothing
*******************************************************************************/
void CLOCKS_OSC8M_Init(void)
{
#define MY_SYSCTRL_OSC8M_MASK ((uint32_t)0xCFFF0002ul)
  /* change the clock frequency from 1MHz to 8MHz */
  SYSCTRL->OSC8M.reg = (MY_SYSCTRL_OSC8M_MASK & SYSCTRL->OSC8M.reg) |
                       SYSCTRL_OSC8M_ONDEMAND |
                       SYSCTRL_OSC8M_RUNSTDBY |
                       SYSCTRL_OSC8M_PRESC_0;
}


/*******************************************************************************
Object:
Parameters: none
Return: Nothing
*******************************************************************************/
void CLOCKS_ENABLE_GCLK_3(void)
{
  GCLK_GENDIV_Type gendiv_3 =
  {
    .bit.DIV = 1,               /* Division Factor */
    .bit.ID = GCLK_GENERATOR_3, /* Generic Clock Generator Selection */
  };
  GCLK->GENDIV.reg = gendiv_3.reg;

  /* Select GCLK3 source to be  ADC */
  GCLK_GENCTRL_Type genctrl_3 =
  {
    .bit.RUNSTDBY = true, /* Run in Standby */
    .bit.DIVSEL = 0,      /* Divide Selection */
    .bit.OE = false,      /* Output Enable */
    .bit.OOV = false,     /* Output Off Value */
    .bit.IDC = true,      /* Improve Duty Cycle */
    .bit.GENEN = enable,  /* Generic Clock Generator Enable */
    .bit.SRC = GCLK_SOURCE_OSC8M,
    .bit.ID = GCLK_GENERATOR_3,
  };
  GCLK->GENCTRL.reg = genctrl_3.reg;
  /* wait for synchronization to complete */
  while (GCLK->STATUS.bit.SYNCBUSY);
}


/*******************************************************************************
Object:
Parameters: none
Return: Nothing
*******************************************************************************/
int main(void)
{
  /* Initialize the OSC8M to clock CPU at 8MHz */
  CLOCKS_OSC8M_Init();
  /* Enable GCLK 3 to run at 8MHz and feed it to the ADC peripheral */
  CLOCKS_ENABLE_GCLK_3();
  /* Enable the ADC with my configuration settings */
  ADC_Init();

  while (1)
  {
    /* perform an ADC reading and get the result */
    myResult = ADC_ReadInput();
  }
}
void ADC_Init(void)
{
	/* Turn on the digital interface clock */
	PM->APBCMASK.reg |= PM_APBCMASK_ADC;

	/* Turn on the analog interface clock and use GCLK Generator 3
	   * source is GCLK_SOURCE_OSC8M
	   */
	GCLK_CLKCTRL_Type clkctrl =
	{
		.bit.ID = ADC_GCLK_ID,
		.bit.GEN = GCLK_GENERATOR_3,
		.bit.CLKEN = true,
		.bit.WRTLOCK = false,
	};
	GCLK->CLKCTRL.reg = clkctrl.reg;

	/* Configure channels that are used for ADC */
	PORT->Group[0].PMUX[1].reg = PORT_PMUX_PMUXO_B | PORT_PMUX_PMUXE_B;
	PORT->Group[0].PINCFG[PIN_PA02].reg = PORT_PINCFG_PMUXEN;
	PORT->Group[0].PINCFG[PIN_PA03].reg = PORT_PINCFG_PMUXEN;

	/* Configure reference */
	ADC_REFCTRL_Type refctrl =
	{
		.bit.REFCOMP = true,
		.bit.REFSEL = REFSEL_INT1V,
	};
	ADC->REFCTRL.reg = refctrl.reg;

	/* Set up the average and samples */
	ADC_AVGCTRL_Type avgctrl =
	{
		.bit.ADJRES = AVGCTRL_DIVIDE_BY_1,
		.bit.SAMPLENUM = AVGCTRL_SAMPLENUM_1,
	};
	ADC->AVGCTRL.reg = avgctrl.reg;

	/* Configure sample length */
	ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(0);

	/* wait for Synchronization */
	while (ADC->STATUS.reg & ADC_STATUS_SYNCBUSY)
	{
		/* Wait for synchronization */
	}

	/* Configure CTRLB */
	ADC_CTRLB_Type ctrlb =
	{
		/* ADC clock is 8MHz / 4 = 2MHz */
		.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV4_Val,
		.bit.RESSEL =  ADC_CTRLB_RESSEL_12BIT_Val,
		.bit.CORREN = false,
		.bit.FREERUN = false,
		.bit.LEFTADJ = false,
		.bit.DIFFMODE = true,
	};
	ADC->CTRLB.reg =  ctrlb.reg;

	/* wait for Synchronization */
	while (ADC->STATUS.reg & ADC_STATUS_SYNCBUSY)
	{
		/* Wait for synchronization */
	}

	/* Configure pin scan mode and positive and negative input pins */
	ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN_1X |
	                     ADC_INPUTCTRL_MUXPOS_PIN0 |
	                     ADC_INPUTCTRL_MUXNEG_PIN1;

	/* wait for Synchronization */
	while (ADC->STATUS.reg & ADC_STATUS_SYNCBUSY)
	{
		/* Wait for synchronization */
	}

	/* Load in the fixed device ADC calibration constants */
	ADC->CALIB.reg =
	  ADC_CALIB_BIAS_CAL((*(uint32_t *) ADC_FUSES_BIASCAL_ADDR >> ADC_FUSES_BIASCAL_Pos)) |
	  ADC_CALIB_LINEARITY_CAL((*(uint64_t *) ADC_FUSES_LINEARITY_0_ADDR >> ADC_FUSES_LINEARITY_0_Pos));

	/* enable the ADC */
	ADC->CTRLA.reg = ADC_CTRLA_ENABLE;

	/* wait for Synchronization */
	while (ADC->STATUS.reg & ADC_STATUS_SYNCBUSY)
	{
		/* Wait for synchronization */
	}
}


/*******************************************************************************
Object: Convert an analog channel and wait for it to finish
Parameters: none
Return: ADC result
*******************************************************************************/
int16_t ADC_ReadInput(void)
{
	ADC->SWTRIG.reg = ADC_SWTRIG_START | ADC_SWTRIG_FLUSH;

	/* wait for Synchronization */
	while (ADC->STATUS.reg & ADC_STATUS_SYNCBUSY)
	{
		/* Wait for synchronization */
	}

	while (ADC->INTFLAG.bit.RESRDY == 0)
	{
		/* Wait for analog conversion to complete */
	}

	/* return with converted value */
	return ADC->RESULT.reg;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I also had a similar problem when playing with a SAM L21 board.  For the L21, the data sheet says that Ain[0] should be PA02.  But on the Xplanined board, that pin doesn't come out to a header pin (unless I'm blind).  So I changed the ADC pin to Ain[1].  Unfortunately that example doesn't show you how to do that, but I've scrounged around and learned a bit more and added this:

 

    config_adc.positive_input = ADC_POSITIVE_INPUT_PIN1;
    config_adc.resolution = ADC_RESOLUTION_12BIT;

After I got the default values with this command

adc_get_config_defaults(&config_adc);

 

Then I can read values on PA03 with GND as my reference.  

 

Also just touching the PA03 pin doesn't move the ADC off the rail.  I had actively drive that pin.  

 

 

hope that helps,

Sheldon

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I fought with the ADC on the SAMD10 for many hours, and finally figured out that the headers that ship with ASF are wrong for SAMD10.

 

utils/cmsis/samd10/include/component/adc.h:

 

#define   ADC_INPUTCTRL_MUXNEG_GND_Val    0x02ul  /**< \brief (ADC_INPUTCTRL) Internal Ground */
#define   ADC_INPUTCTRL_MUXNEG_IOGND_Val  0x03ul  /**< \brief (ADC_INPUTCTRL) I/O Ground */

 

However, the datasheet has these values at 0x18 and 0x19.  Correcting the headers to those values makes the ADC start working.

 

#define   ADC_INPUTCTRL_MUXNEG_GND_Val    0x18ul  /**< \brief (ADC_INPUTCTRL) Internal Ground */
#define   ADC_INPUTCTRL_MUXNEG_IOGND_Val  0x19ul  /**< \brief (ADC_INPUTCTRL) I/O Ground */

 

Very frustrating, so I'm posting here in case others come across this thread, as I did, when they can't get the ADC to work.