Author Topic: [SAM] [ADC] register conundrums  (Read 3429 times)

0 Members and 1 Guest are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
[SAM] [ADC] register conundrums
« on: February 02, 2022, 08:35:29 am »
I attach 3 sections of the ADC description for the SAMC, So this offset and gain calculation. I assume from the lack of mention that this something that has to be manually added ? It's not that the ADC automatically works out it's gain and offset error?

There seem to be 2 offset features the other is definitely automatic, I assume the input is shorted to ground and a reading taken to subtract from the value automatically. As they tell you all about this I assume the other is not automatic and I would have to devise a self calibration routine.

So the gain register. that text is incomprehensible to me. I understood it as there is an 11 bit value that, actually i don't know, I give up.

½ <= GAINCORR < 2

Are they trying to say that the gain can be set from 0.5-2?

values range from 0.10000000000 to 1.11111111111

But 0.1 is less than 0.5

2^11 = 2048, did they mean that this is in 2's complement like the offset register? in which case a 10 bit fractional value could be added to "1" to produce a +/- amount around "1" which is the neutral value for gain calculations.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #1 on: February 02, 2022, 08:57:40 am »
Buffer compensation and offset correction are completely different independent features. The first one is analog and affects conversion results. The second one is purely digital applied after the conversion is done.

Gain correction is a fixed point fractional number. 0.10..000 and 1.111..1111 are binary numbers.  0.10..000 in binary is a 0.5 fractional value. Binary 1.00..00 is 1.0 decimal. To divide 1.0 by 2 you shift 1.00..00 to the right, and you get 0.10..000. Same with multiplication by 2 - if you shift 1.00..00 to the left, you get 10.00..00, but this is not representable in this fixed point format, so they use 1.111...111 (shifted number minus one), so the actual multiplication is done by 1.999...999, and not 2. That's why it says "0.5 <=", but "< 2".

Look up how fixed point fractional numbers work if you want to understand this in detail.

Alternatively you can read uncorrected numbers and apply any correction you like in the firmware.  You will not lose in analog accuracy, since this correction is digital anyway. It is nothing you can't do on the MCU itself.

This format of the fixed point number would be called "UQ1.11". See https://en.wikipedia.org/wiki/Q_(number_format) for details.
« Last Edit: February 02, 2022, 09:07:54 am by ataradov »
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #2 on: February 02, 2022, 09:24:24 am »
Right so 3rd attempt at this. Are they saying (but not saying) that you cannot put less than 10000000000 in the register? so it will be, no that still does not work, none of these bits can be a fraction whilst also determining the whole number not be 0 or 1. The only way this could work is that the number is in 2's complement as a fraction added to 1, if you add a negative fraction to 1 you will make it 0.something. But they do not say that the number is in 2's complement.

 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #3 on: February 02, 2022, 09:33:33 am »
I guess, technically you should be able to put numbers less than 0.10000000000, which would represent gain < 0.5, but the result depends on how their multiplier is implemented. So yes, you should not configure gain < 0.5. I'm not sure why you would ever want to do that anyway.

And yes, it works. Read how fixed points numbers work. Integer part is 1 bit (most significant one). It can be either 0 or 1, so full range represented by the fixed point number is 0.0 to 1.99(9). And in this case lower end is limited to 0.5 for some reason, but that's a separate story.
« Last Edit: February 02, 2022, 09:36:07 am by ataradov »
Alex
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8180
  • Country: fi
Re: [SAM] [ADC] register conundrums
« Reply #4 on: February 02, 2022, 09:38:07 am »
Also don't fall into the trap of premature optimization. Gain and offset corrections are nice features, but the whole idea is they just save a few CPU cycles.

Just start by using the values as-is. If you need calibration, do it in software, less need to read and understand manuals, more control about what happens.

Only if you actually have a performance issue, then think about utilizing in-peripheral processing features.

Regarding the fixed-point value, it might be easier if you just think about it as a number, where 1024 corresponds to exactly 1.0, and 2048 would correspond to exactly 2.0. 0 would correspond 0. It's a simple linear system. If you need 1.1, that would be approximately 1024+102.
« Last Edit: February 02, 2022, 09:41:18 am by Siwastaja »
 
The following users thanked this post: hans, thm_w

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #5 on: February 02, 2022, 09:43:32 am »
my problem is not fixed point numbers of which there are various ways of representing them.

So the 11th digit is the whole part, the lower 10 are a 10 bit fraction (not 11!) and the minimum value is 01000000000 which is 0.1000000000 not 0.10000000000 as shown.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #6 on: February 02, 2022, 09:44:57 am »
Also don't fall into the trap of premature optimization. Gain and offset corrections are nice features, but the whole idea is they just save a few CPU cycles.

Just start by using the values as-is. If you need calibration, do it in software, less need to read and understand manuals, more control about what happens.

Only if you actually have a performance issue, then think about utilizing in-peripheral processing features.

Regarding the fixed-point value, it might be easier if you just think about it as a number, where 1024 corresponds to exactly 1.0, and 2048 would correspond to exactly 2.0. 0 would correspond 0. It's a simple linear system. If you need 1.1, that would be approximately 1024+102.

i don't intend using this feature yet. I'm just trying to get into the head oaf the person writing this.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #7 on: February 02, 2022, 09:46:18 am »
It is a 12 bit number with bits going from 0 to 11. Most significant bit is integer part, 11 least significant bits are fractional part. The datasheet is correct.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #8 on: February 02, 2022, 10:19:26 am »
Sorry yes I was fooled by bit position 11, yes it is 12 bits, so minimum value is 010000000000 with a decimal place added after the first digit.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #9 on: February 02, 2022, 10:21:09 am »
Right, next. Rail to rail operation setting, you what? so what does it go to without this? I don't understand, I read that as normally you can take a frigging guess at what you will get, enable this handy feature for it to work as you would expect - but you will have to disable a load of other features and just to test your skills we use a double negative in the explanation.
« Last Edit: February 02, 2022, 10:55:11 am by Simon »
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #10 on: February 02, 2022, 03:02:42 pm »
Please stop complaining.  I'm not going to respond to all that negativity. I get it, you don't like the datasheet, no need to point this out in every single post, especially given that most of the time you can't read.

Also, your question itself is not exactly the most clear either.

Effects of R2R setting are shown in the Table 45-19. It affects input common mode voltage. With R2R = 1 you get common mode input in the range 0.2V - VREF-0.2V. With R2R = 0 you get VCM = VREF/2-0.2V - VREF/2+0.2V.

Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #11 on: February 02, 2022, 03:33:49 pm »
I showed the text of the datasheet that describes this, that is the sum total in the ADC section about it. It makes it sound like I won't get to read the full input voltage range of 0-Vref unless I used it, which I am sure is not the case.

I take it as we are talking about differential input voltage differences/references? as even in the electrical characteristics it sounds pretty hairy. As far as I am aware any voltage on the micro is "common mode", all voltages have a common reference to ground right?

Or are they trying to say that when used in differential mode the minimum voltage of the two voltages measured must be in a certain voltage range?
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #12 on: February 02, 2022, 04:44:12 pm »
The ADC itself is fully differential. You can route both V+ and V- on the external pins. The common voltage on those pins is the parameter in question.Single-ended mode is achieved by grounding the V- input internally.

ADC will always convert the full scale range, but the accuracy (offset/linearity) would be worse at the ends of the range. Using R2R setting allows you  to get better results.

For most practical applications you won't see a difference, but there is no hard in just enabling R2R all the time.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #13 on: February 02, 2022, 05:23:31 pm »
Well if I enable R2R I cannot use other things like result accumulation so I guess it is a trade off in the complexity of getting single accurate result and doing your own filtering or using the hardware filtering. I'm trying to read the VDDANA/4 voltage which presumably is available for things like calibrations I may want to make but the results are drifting around quite a bit. I am going to setup a voltage source for it to read tomorrow so that I know what I am putting in and can read the converted value to check my setup.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #14 on: February 02, 2022, 05:39:28 pm »
What are your ADC settings? What is your reference?  Make sure you don't overclock the ADC.

And how much thing are drifting? Can you provide the ranges or values?
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #15 on: February 02, 2022, 07:32:04 pm »
Well I get a figure of around 2000 in 12 bit mode but it can jump by about 100 each way. I need to do a lot of averaging of the result anyway. The setup is pretty much the basic. I can report on those tomorrow when I am back at work.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #16 on: February 03, 2022, 08:22:00 am »
So I have a 12MHz clock (48/4) i's in 12 bit mode and everything is off in terms of additional functionality. The reference is the analogue supply at 3.3V and I have set SCALEDVDDCORE as the input which is the core voltage divided by 2. I believe the core is at 1.2V? I take a reading once a second, I get numbers between 1814 and 2118. so let's say 2000 on average:

2000 / 4095 x 3.3 = ~1.6V or around SCALEDVDDCORE /2, maybe that is what it is meant to be and not SCALEDVDDCORE /4 ? but setting SCALEDVDDANA gets the same numbers and should be about 3 times.

I'll put an actual voltage in today.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #17 on: February 03, 2022, 08:25:53 am »
Changing some of my code around gives a different result, around 730 with fluctuations in the lows 10's so I've obviously got a problem somewhere.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #18 on: February 03, 2022, 08:28:50 am »
Scaled VDDCORE is scaled by 1/4 for sure. It is better to provide your initialization code.
Alex
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8180
  • Country: fi
Re: [SAM] [ADC] register conundrums
« Reply #19 on: February 03, 2022, 09:50:02 am »
What is the source signal? Is it properly low impedance, either by opamp buffering, or if DC enough, just filtered with a capacitor of at very least 100nF?
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #20 on: February 03, 2022, 10:32:54 am »
OK I have got it down to the minimum and now have 1004/5 as a result measureng VDDANA/4 which is pretty much 1/4 4095 which is the refence voltage VDDANA. It's still quite a few counts out but I assume that there is no massive promise on accuracy of the VDDANA/4 ? Or there is some source impedance there.

Code: [Select]
void ADC0_init()
{
REG_MCLK_APBCMASK |= 0x1 << 17 ;
REG_GCLK_PCHCTRL33 = ADC0_GCLK_GEN | 0x1 << 6 ; // Connect ADC0 to GCLK GEN and enable clock - see clock_config.h
REG_ADC0_CTRLB = 0x2 ;

// CTRLB ADC CLK pre-scaler

REG_ADC0_REFCTRL = 0x5 ;

// INPUTCTRL input control

REG_ADC0_INPUTCTRL = 0x1B ;


// CTRLA

REG_ADC0_CTRLA = 0x1 << 1 ; // enable ADC0
}



The mess this came from was:

adc.h

Code: [Select]

// channel selection bits
#define SCALEDVDDANA  0x1B // 1/4 of VDDANA
#define SCALEDVDDCORE 0x1A // 1/4 Vcore

// ADC 0 setup
void ADC0_init1() ;
void ADC0_init() ; // initialize and enable the ADC with the below settings (not complete)

#define ADC0_CLK_DIV 4 // may be any power of 2 from 2-256 resulting frequency must be between 160kHz and 16MHz

#define ADC0_Reference_Buffer_Offset_Compensation_Enable 0 // Reference Buffer Offset Compensation Enable CTRLB

#define ADC0_rail_to_rail_operation_enable 0 // enable rail to rail operation.

#define ADC0_Sampling_Time_Length 0 // extend sampling time beyond 1 clock cycle from 0 - 64 (cannot be used with above two options)

#define ADC0_Reference_Selection VDDANA // Reference Selection REFCTRL

#define ADC0_Init_POS_channel SCALEDVDDANA // use 0-11 or above def's

#define ADC0_Init_NEG_channel 0 // use 0-5

#define ADC0_Conversion_Result_Resolution 12 // 8, 10, 12 or 16 for averaging and oversampling

#define ADC0_Free_Running_Mode_Enable 0 // enable (1) disable free running mode

#define ADC0_Differential_Mode_Enable 0 // enable differential mode

#define ADC0_Digital_Correction_Logic_Enabled 0 // enable manual offset and gain corrections with values in register.

#define ADC0_samples_to_accumulate 1 // samples will be accumulated, number must always be a power of 2 from 1 - 1024

#define ADC0_Result_division 0 // right shifting of result use 0-7

#define ADD_REG_ADC0_CTRLA             (0x42004400UL) /**< \brief (ADC0) Control A */
#define ADD_REG_ADC0_CTRLB             (0x42004401UL) /**< \brief (ADC0) Control B */
#define ADD_REG_ADC0_REFCTRL           (0x42004402UL) /**< \brief (ADC0) Reference Control */
#define ADD_REG_ADC0_EVCTRL            (0x42004403UL) /**< \brief (ADC0) Event Control */
#define ADD_REG_ADC0_INTENCLR          (0x42004404UL) /**< \brief (ADC0) Interrupt Enable Clear */
#define ADD_REG_ADC0_INTENSET          (0x42004405UL) /**< \brief (ADC0) Interrupt Enable Set */
#define ADD_REG_ADC0_INTFLAG           (0x42004406UL) /**< \brief (ADC0) Interrupt Flag Status and Clear */
#define ADD_REG_ADC0_SEQSTATUS         (0x42004407UL) /**< \brief (ADC0) Sequence Status */
#define ADD_REG_ADC0_INPUTCTRL         (0x42004408UL) /**< \brief (ADC0) Input Control */
#define ADD_REG_ADC0_CTRLC             (0x4200440AUL) /**< \brief (ADC0) Control C */
#define ADD_REG_ADC0_AVGCTRL           (0x4200440CUL) /**< \brief (ADC0) Average Control */
#define ADD_REG_ADC0_SAMPCTRL          (0x4200440DUL) /**< \brief (ADC0) Sample Time Control */
#define ADD_REG_ADC0_WINLT             (0x4200440EUL) /**< \brief (ADC0) Window Monitor Lower Threshold */
#define ADD_REG_ADC0_WINUT             (0x42004410UL) /**< \brief (ADC0) Window Monitor Upper Threshold */
#define ADD_REG_ADC0_GAINCORR          (0x42004412UL) /**< \brief (ADC0) Gain Correction */
#define ADD_REG_ADC0_OFFSETCORR        (0x42004414UL) /**< \brief (ADC0) Offset Correction */
#define ADD_REG_ADC0_SWTRIG            (0x42004418UL) /**< \brief (ADC0) Software Trigger */
#define ADD_REG_ADC0_DBGCTRL           (0x4200441CUL) /**< \brief (ADC0) Debug Control */
#define ADD_REG_ADC0_SYNCBUSY          (0x42004420UL) /**< \brief (ADC0) Synchronization Busy */
#define ADD_REG_ADC0_RESULT            (0x42004424UL) /**< \brief (ADC0) Result */
#define ADD_REG_ADC0_SEQCTRL           (0x42004428UL) /**< \brief (ADC0) Sequence Control */
#define ADD_REG_ADC0_CALIB             (0x4200442CUL) /**< \brief (ADC0) Calibration */

#define ADD_REG_ADC_OFFSET 0X400




static inline void ADC_start_conversion(uint8_t channel)
{
register8( ADD_REG_ADC0_SWTRIG + channel * ADD_REG_ADC_OFFSET ) = 0x1 << 1 ;
}

static inline void ADC_POS_channel_select( uint8_t adc_ch,  uint8_t adc_pin)
{
register16( ADD_REG_ADC0_INPUTCTRL + adc_ch * ADD_REG_ADC_OFFSET ) &= 0xFFFF0000 | adc_pin ;
}


static inline void ADC_POS_NEG_channel_select( uint8_t adc_ch,  uint8_t pos_pin,  uint8_t neg_pin )
{
register16( ADD_REG_ADC0_INPUTCTRL + adc_ch * ADD_REG_ADC_OFFSET ) = pos_pin | neg_pin << 8 ;
}

static inline uint16_t ADC_result_ret( uint8_t adc_ch)
{
return register16( ADD_REG_ADC0_RESULT + adc_ch * ADD_REG_ADC_OFFSET )  ;
}

static inline uint16_t ADC0_result_ret()
{
return REG_ADC0_RESULT   ;
}



adc.c

Code: [Select]
void ADC0_init()
{
REG_MCLK_APBCMASK |= 0x1 << 17 ;
REG_GCLK_PCHCTRL33 = ADC0_GCLK_GEN | 0x1 << 6 ; // Connect ADC0 to GCLK GEN and enable clock - see clock_config.h

// CTRLB ADC CLK pre-scaler

#if (ADC0_CLK_DIV == 2)
REG_ADC0_CTRLB = 0x0 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 4)
REG_ADC0_CTRLB = 0x1 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 8)
REG_ADC0_CTRLB = 0x2 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 16)
REG_ADC0_CTRLB = 0x3 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 32)
REG_ADC0_CTRLB = 0x4 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 64)
REG_ADC0_CTRLB = 0x5 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 128)
REG_ADC0_CTRLB = 0x6 ;
#define ADC0_CLK_OK
#endif
#if (ADC0_CLK_DIV == 256)
REG_ADC0_CTRLB = 0x7 ;
#define ADC0_CLK_OK
#endif
#ifndef ADC0_CLK_OK
#warning ADC0 clock precaler incorrectly defined see adc_config.h
#endif

// ADC0 REFCTRL

#if (ADC0_Reference_Selection == VDDANA)
#define ADC0_REFCTRL_REFSEL 0x5
#endif

REG_ADC0_REFCTRL = ADC0_REFCTRL_REFSEL | ADC0_Reference_Buffer_Offset_Compensation_Enable << 7 ;

// INPUTCTRL input control

REG_ADC0_INPUTCTRL = ADC0_Init_POS_channel | ADC0_Init_NEG_channel << 8 ;

// CTRLC

#if (ADC0_Conversion_Result_Resolution == 12)
#define ADC0_RESSEL_s 0x0
#endif
#if (ADC0_Conversion_Result_Resolution == 16)
#define ADC0_RESSEL_s 0x1
#endif
#if (ADC0_Conversion_Result_Resolution == 10)
#define ADC0_RESSEL_s 0x2
#endif
#if (ADC0_Conversion_Result_Resolution == 8)
#define ADC0_RESSEL_s 0x3
#endif
#ifndef ADC0_RESSEL_s
#warning incorrect  resolution setting
#endif

REG_ADC0_CTRLC = ADC0_rail_to_rail_operation_enable << 7 | ADC0_RESSEL_s << 4 | ADC0_Free_Running_Mode_Enable << 2 | ADC0_Differential_Mode_Enable <<  0 ;

#if ( ( ADC0_rail_to_rail_operation_enable == 1 ) && ( ADC0_Reference_Buffer_Offset_Compensation_Enable == 0 ) )
#warning ADC0_Reference_Buffer_Offset_Compensation_Enable must be turned on to use ADC0_rail_to_rail_operation_enable
#endif

// AVGCTRL averaging control

#if ( ADC0_samples_to_accumulate == 1 )
#define ADC0_samples 0
#endif
#if ( ADC0_samples_to_accumulate == 2 )
#define ADC0_samples 1
#endif
#if ( ADC0_samples_to_accumulate == 4 )
#define ADC0_samples 2
#endif
#if ( ADC0_samples_to_accumulate == 8 )
#define ADC0_samples 3
#endif
#if ( ADC0_samples_to_accumulate == 16 )
#define ADC0_samples 4
#endif
#if ( ADC0_samples_to_accumulate == 32 )
#define ADC0_samples 5
#endif
#if ( ADC0_samples_to_accumulate == 64 )
#define ADC0_samples 6
#endif
#if ( ADC0_samples_to_accumulate == 128 )
#define ADC0_samples 7
#endif
#if ( ADC0_samples_to_accumulate == 256 )
#define ADC0_samples 8
#endif
#if ( ADC0_samples_to_accumulate == 512 )
#define ADC0_samples 9
#endif
#if ( ADC0_samples_to_accumulate == 1024 )
#define ADC0_samples 10
#endif
#ifndef ADC0_samples
#warning ADC0 samples accumulator incorrectly defined see adc_config.h
#endif

#if (ADC0_Conversion_Result_Resolution == 16)
REG_ADC0_AVGCTRL = ADC0_samples | ADC0_Result_division << 4 ;
#endif



// SAMPCTRL sampling control

REG_ADC0_SAMPCTRL =  ADC0_Sampling_Time_Length | ADC0_Reference_Buffer_Offset_Compensation_Enable << 7 ;

#if ( ADC0_Sampling_Time_Length && ADC0_Reference_Buffer_Offset_Compensation_Enable )
#warning sampling time length cannot be used with reference buffer offset compensation (therefore rail to rail operation must also be disabled )
#endif

// CTRLA

REG_ADC0_CTRLA = 0x1 << 1 ; // enable ADC0
}

 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #21 on: February 03, 2022, 10:49:30 am »
looks like I didn't understand how the pre-processor works and it fails on the ctrlb register macros to set the correct clock setting
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #22 on: February 03, 2022, 11:24:35 pm »
1/4 divider is not the most accurate, and it is not characterized.

You need to select MUXNEG=GND (not sure if it is really needed for a single-ended mode, but will not hurt) and you are not writing anything into CALIB register.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #23 on: February 04, 2022, 07:58:42 am »
I didn't think it would be. Connecting the negative input to GND does not make any difference in non diff mode, but no I've not loaded any calibration and I guess there is a reason why that calibration is available.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #24 on: February 04, 2022, 08:27:47 am »
Well my VDDANA/4 has now moved from 1004 to 1006, slight improvement towards the expected 1023 :)

Code: [Select]

#define register8(x)  ( *(volatile uint8_t * )( x ) )

#define REG_ADC0_CAL register8(0x806020)

REG_ADC0_CALIB = (uint16_t) ( ( REG_ADC0_CAL & 0x7 ) << 8 | ( REG_ADC0_CAL & 0b00111000 ) >> 3 ) ;


I take it I am OK to treat the section of the 64 bit calibration data that I want as an 8 bit register.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #25 on: February 05, 2022, 12:36:50 am »
Yes, this looks fine. I think in practice both those things should be 0b111.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #26 on: February 06, 2022, 02:08:38 pm »
Well yes they are, I just used 0x7 where it was practical for the bits in the lower 3 positions but could not be bothered to look up the hex of 0b111000, I suppose as I am shifting the value that I have constrained to 8 bits to the right I don't need to AND them with the mask but went belt and braces.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #27 on: February 06, 2022, 06:00:18 pm »
No, I mean the calibration values would be 0b111. It doers not matter how you extract them.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #28 on: February 06, 2022, 07:48:28 pm »
Oh, but if the calibration values are just 111 then what is the point? the datasheet says to load these values and cautions against changing them but if they are just 111 anyway I could just load that.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #29 on: February 06, 2022, 08:36:20 pm »
Because they might change in the future revisions of the device. It is a good idea to load them, or course. But it is also a good check that you are loading correct values at least for now.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #30 on: February 06, 2022, 09:01:51 pm »
OK, well luckily I have 40 characters of display I can put values on. I'll give that a go tomorrow.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #31 on: February 07, 2022, 08:08:22 am »
Bits 2:0 are 0b101, bits 5:3 are 0b111
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #32 on: February 07, 2022, 08:19:53 am »
Interesting. May be they actually have different values on some devices.

Can you read the same values for the ADC1? Or the whole 64-bit word will do as well.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #33 on: February 07, 2022, 09:15:16 pm »
I'll give it a go in the morning
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #34 on: February 08, 2022, 08:02:36 am »
The lower 16 bits read out as 0101 1111 1011 1110 so yes those values are the same in both ADC's
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #35 on: February 10, 2022, 04:56:52 pm »
So I have 2 channels to measure alternately. I see that the ADC has a sequence option, this is handy as it takes an amount of coding out of doing it otherwise.

But there is only one result register, so an interrupt fires to say the conversion is done, but the next conversion has already started, is the result register OK to read before the second conversion is complete or will it be altered. Same applies for a continuous sampling of just one channel, if the new conversion starts as soon as the last one has completed presumably if the result register is read before the current conversion is complete it has a valid result.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8180
  • Country: fi
Re: [SAM] [ADC] register conundrums
« Reply #36 on: February 10, 2022, 05:27:07 pm »
Usually you want to use sequences with DMA.

Using this pattern (note, I have not used Atmel SAM devices, but works on STM32, and the idea should work on any decent modern micro):
Code: [Select]
typedef struct __attribute__((packed))
{
   uint16_t voltage;
   int16_t current;
} adc_datapoint_t;

#define BUFLEN 1

volatile adc_datapoint_t adc_buf[BUFLEN];

void init_adc()
{
   DMA.memory_address = adc_buf;
   DMA.transfer_length = sizeof adc_buf / sizeof adc_buf[0]; // also known as number of elements
   DMA.transfer_size = 16 bits (in this case, because of 16-bit ADC)
   DMA.mode = continuous / wrap around / whatever it is called.

   Start DMA

   Configure ADC channel sequence to match that struct
   Configure ADC in DMA mode
   Etc.
   Start ADC
}


If you do this, you can just access the struct directly, getting latest measurements, whenever and wherever you want! No need to manage anything, except when you need to know that all values within the struct are from the same "round", converted in the same order, then you can use DMA completion interrupt, for example.

But in my projects, I have noticed that just having the latest value is often exactly what is needed.
« Last Edit: February 10, 2022, 05:29:16 pm by Siwastaja »
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [SAM] [ADC] register conundrums
« Reply #37 on: February 10, 2022, 05:27:36 pm »
The result register is updated at the end of the conversion cycle. You have the full cycle to read the previous result.
Alex
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #38 on: February 10, 2022, 07:45:15 pm »
The result register is updated at the end of the conversion cycle. You have the full cycle to read the previous result.

Goodho, that will do me. Thinking about it a bit I would still have to write the code to cycle through the channels. I either tell the ADC what to do next or have to work out what channel just converted. The former has less headaches I think and code overhead.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #39 on: February 10, 2022, 07:46:37 pm »
Usually you want to use sequences with DMA.

Using this pattern (note, I have not used Atmel SAM devices, but works on STM32, and the idea should work on any decent modern micro):
Code: [Select]
typedef struct __attribute__((packed))
{
   uint16_t voltage;
   int16_t current;
} adc_datapoint_t;

#define BUFLEN 1

volatile adc_datapoint_t adc_buf[BUFLEN];

void init_adc()
{
   DMA.memory_address = adc_buf;
   DMA.transfer_length = sizeof adc_buf / sizeof adc_buf[0]; // also known as number of elements
   DMA.transfer_size = 16 bits (in this case, because of 16-bit ADC)
   DMA.mode = continuous / wrap around / whatever it is called.

   Start DMA

   Configure ADC channel sequence to match that struct
   Configure ADC in DMA mode
   Etc.
   Start ADC
}


If you do this, you can just access the struct directly, getting latest measurements, whenever and wherever you want! No need to manage anything, except when you need to know that all values within the struct are from the same "round", converted in the same order, then you can use DMA completion interrupt, for example.

But in my projects, I have noticed that just having the latest value is often exactly what is needed.

Ah, yea, DMA, I'll get to that at some point :) . At the moment I don't need to squeeze for performance that hard.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8180
  • Country: fi
Re: [SAM] [ADC] register conundrums
« Reply #40 on: February 11, 2022, 07:41:44 am »
Ah, yea, DMA, I'll get to that at some point :) . At the moment I don't need to squeeze for performance that hard.

The point was, not for performance, but because it is easier! Once you set it up, you don't need to think about timing or write any ISR at all, latest conversions are accessible as normal variables.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17821
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [SAM] [ADC] register conundrums
« Reply #41 on: February 11, 2022, 08:15:55 am »
Yes code complexity is my main concern but I don't have time to take on another peripheral right now. For the current purposes I can use code in an interrupt. After this project is done I can look at the DMA as I will also start to look at CAN bus.

Broadly to me: code != performance. If there is a feature that lets you do something without writing code for it it increases performance as it means it is one less thing the CPU is having to do, it just happens. For example on an AVR I wrote code to average several ADC readings, this required CPU intervention each time a result was ready. On the SAMC the ADC will take multiple readings and average them automatically into the result register and then fire the conversion complete interrupt. So I can now accumulate 1024 samples and the CPU only has to do something about it once rather than 1024 times.

I guess the (tiny) penalty of something like automatic sequencing is that you need to allocate RAM space for all of the channels whether they are used or not. With an AVR my first automatic sequencing code meant that I would just run for channel 0 to a "top" channel. I am now creating another array to store a "slot" allocation so that I can choose the sequence and which channels. The SAMC again does this for me but then without looking into it I am unsure how much memory I now need for the results, not that it is that important, I am not constrained by I avoid being wasteful. Would it need to be result x channels, probably not but I would likely need the DMA to do that, with interrupted code I'd be having to check which channel just returned which will take CPU time so DMA will be the way I guess when I get there.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf