Samd21 2 channel adc + dma. Data getting reordered when sleeping

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi All,

 

I have a successful app running a DMA driven ADC for 2 measurements. I found that when I goto sleep (specifically standby) that the 2 cells that are supposed to be populated can sometimes get reordered, its like the dma has missed one of the ADC conversions or something. I have seen similar things happen in the past when pausing with a debugger, that this data can sometimes be moved around.

 

During sleeping I run my GCLK 1 which has been set up for an internal 8mhz clock, my main clock goes to sleep.

Has anyone seen this or could comment on a work around/fix that I could use to resolve this?

 

ADC structure:

typedef struct __attribute__((__packed__)){
	uint16_t adcVBat;	//PB04 - ADC 12
	uint16_t adcVCharge;	//PB05 - ADC 13
} AdcMeasure_t;

adc+ dma code

#include "app.h"
#include "app_adc.h"

AdcMeasure_t AdcMeasure;

void configure_dma_resource(struct dma_resource *resource);
void setup_transfer_descriptor(DmacDescriptor *descriptor);

void configure_adc(void);

struct adc_module adc_instance;
struct dma_resource example_resource;

COMPILER_ALIGNED(16) DmacDescriptor dma_descriptor SECTION_DMAC_DESCRIPTOR;

//SAMC21
//ADC_POSITIVE_INPUT_PIN6;	//PB04 - VBatt
//ADC_POSITIVE_INPUT_PIN7;	//PB05 - VCharge

//SAMD21
//ADC_POSITIVE_INPUT_PIN12
//ADC_POSITIVE_INPUT_PIN13

void configure_adc(void) {
	struct adc_config config_adc;

	adc_get_config_defaults(&config_adc);

	#if !(SAMC21)
	//config_adc.gain_factor     = ADC_GAIN_FACTOR_1X;
	config_adc.gain_factor     = ADC_GAIN_FACTOR_DIV2;
	#endif
	config_adc.resolution      = ADC_RESOLUTION_12BIT;
	config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV128;	//48M/128 = 375khz
	config_adc.reference       = ADC_REFERENCE_INTVCC0;	//vddana == 3.3, ref vana/1.48 ~= 2.229
	#if (SAMD21)
	config_adc.positive_input  = ADC_POSITIVE_INPUT_PIN12;
	config_adc.pin_scan.inputs_to_scan = 2;
	config_adc.pin_scan.offset_start_scan = 0;
	#elif (SAMC21)
	config_adc.positive_input  = ADC_POSITIVE_INPUT_PIN6;
	config_adc.positive_input_sequence_mask_enable = (1 << ADC_INPUTCTRL_MUXPOS_AIN6_Val) | (1<< ADC_INPUTCTRL_MUXPOS_AIN7_Val);
	#endif
	config_adc.freerunning     = true;
	config_adc.left_adjust     = false;
	config_adc.run_in_standby = true;	//else dma ordering will break
	config_adc.clock_source = GCLK_GENERATOR_1;
	#if (SAMC21)
	adc_init(&adc_instance, ADC1, &config_adc);
	#else
	adc_init(&adc_instance, ADC, &config_adc);
	#endif
	adc_enable(&adc_instance);
}

void configure_dma_resource(struct dma_resource *resource) {
	struct dma_resource_config config;

	dma_get_config_defaults(&config);
	#if (SAMC21)
	config.peripheral_trigger = ADC1_DMAC_ID_RESRDY;
	#else
	config.peripheral_trigger = ADC_DMAC_ID_RESRDY;
	#endif
	
	#ifdef FEATURE_DMA_CHANNEL_STANDBY
	/** Keep DMA channel enabled in standby sleep mode if true */
	config.run_in_standby = true;	//not valid for samd21
	#endif
	
	config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
	dma_allocate(resource, &config);
}


void setup_transfer_descriptor(DmacDescriptor *descriptor) {
	struct dma_descriptor_config descriptor_config;
	dma_descriptor_get_config_defaults(&descriptor_config);
	uint16_t * dest = &((uint16_t*)(&AdcMeasure))[2];
	
	descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
	descriptor_config.dst_increment_enable = true;
	descriptor_config.src_increment_enable = false;
	descriptor_config.block_transfer_count = 2;
	descriptor_config.source_address = (uint32_t)(&adc_instance.hw->RESULT.reg);
	descriptor_config.destination_address = (uint32_t)dest;
	descriptor_config.next_descriptor_address = (uint32_t)descriptor;
	
	dma_descriptor_create(descriptor, &descriptor_config);
}

void ADC_Init(void) {
	configure_adc();
	configure_dma_resource(&example_resource);
	setup_transfer_descriptor(&dma_descriptor);
	dma_add_descriptor(&example_resource, &dma_descriptor);
	adc_start_conversion(&adc_instance);
	dma_start_transfer_job(&example_resource);
}