-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Description
Hello there!
While working with the SAMD21G18A MCU I noticed a weird behavior on the voltage reference selection for the ADC and haven't found any comment, issue or google result regarding this. After a some troubleshooting I decided to post this here to let anyone who's facing the same problem know.
Describe the bug
In short:
ADC_VREF_VDD_1_2actually uses the 1/1.48 VDDANA reference voltage instead of 1/2 VDDANAADC_VREF_VDD_1uses the 1/2 VDDANA reference voltage, instead of being unsupported (as per the MCU datasheet).
This was tested using only the SAMD21G18A MCU, it might work as intended in other sam0 MCUs.
For more information, please see below.
To Reproduce
After getting some weird readings with the SAMD21 ADC, I decided to do some basic tests to find out why this was occurring. The test are based on the internal tests used by twister. These are the basic configurations I used during the test (other than the ADC reference):
#define ADC_RESOLUTION 12
#define ADC_GAIN ADC_GAIN_1
#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT
#define ADC_CHANNEL_ID 0
#define ADC_1ST_CHANNEL_INPUT ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_ValThe chosen input is the internal pin SCALEDIOVCC, which is the ANAVCC divided by 4 (In our case: 3.3/4 = 0.825V). Five readings were made, using ADC_VREF_INTERNAL, ADC_VREF_VDD_1, ADC_VREF_VDD_1_2, ADC_VREF_VDD_1_3 and ADC_VREF_VDD_1_4 each.
Expected behavior
These are the results, remember that all valid readings should convert to approximately 0.825V:
-
ADC_VREF_INTERNAL(1V theoretically)
Raw: 3378, converting to volts: 0.830V (Ok) -
ADC_VREF_VDD_1(Theoretically not supported the MCU)
Raw: 2078, converting to volts: 1.670V (WRONG) -
ADC_VREF_VDD_1_2(1.65V theoretically)
Raw: 1540, converting to volts: 0.620V (WRONG) -
ADC_VREF_VDD_1_3(Theoretically not supported the MCU)
Error: unsupported reference. (Ok) -
ADC_VREF_VDD_1_4(Theoretically not supported the MCU)
Error: unsupported reference. (Ok)
Impact
As there is no note about the use of macros defined by enum adc_reference
with the SAMD21 processor and they don't define the voltages as the documentation suggests (see below in Additional context), it's very easy to be stuck in ADC reading problems without delving deep into the Zephyr source to find out why.
New users could easily spend a lot of time troubleshooting this problem when this is only a matter of mislabeling.
Environment:
- OS: Linux
- Toolchain: Zephyr SDK
- Version: zephyr-v3.0.0-1614-g6d7d41420415
Additional context
After following a trail of #include's, I found out that the macros needed, used by adc_sam0_channel_setup() to control the reference voltage, are defined in adc_fixup_sam0.h:
#ifndef ADC_REFCTRL_REFSEL_INTERNAL
# ifdef ADC_REFCTRL_REFSEL_INTREF
# define ADC_REFCTRL_REFSEL_INTERNAL ADC_REFCTRL_REFSEL_INTREF
# else
# define ADC_REFCTRL_REFSEL_INTERNAL ADC_REFCTRL_REFSEL_INT1V
# endif
#endif
#ifndef ADC_REFCTRL_REFSEL_VDD_1_2
# ifdef ADC_REFCTRL_REFSEL_INTVCC0
# define ADC_REFCTRL_REFSEL_VDD_1_2 ADC_REFCTRL_REFSEL_INTVCC0
# else
# define ADC_REFCTRL_REFSEL_VDD_1_2 ADC_REFCTRL_REFSEL_INTVCC1
# endif
#endif
#ifndef ADC_REFCTRL_REFSEL_VDD_1
# ifdef ADC_REFCTRL_REFSEL_INTVCC1
# define ADC_REFCTRL_REFSEL_VDD_1 ADC_REFCTRL_REFSEL_INTVCC1
# endif
#endifFollowing the definitions through the files and consulting the MCU datasheet, the final definitions for each macro is:
ADC_REFCTRL_REFSEL_INTERNAL=ADC_REFCTRL_REFSEL_INT1V= 1V (Ok)ADC_REFCTRL_REFSEL_VDD_1_2=ADC_REFCTRL_REFSEL_INTVCC0= 1/1.48 VDDANA (WRONG, should beADC_REFCTRL_REFSEL_INTVCC1)ADC_REFCTRL_REFSEL_VDD_1=ADC_REFCTRL_REFSEL_INTVCC1= 1/2 VDDANA (WRONG, should be invalid for the SAMD21).
Also, there is no way (that I'm aware) besides manually changing the register to use the 1/1.48 VDDANA reference voltage using the current enum adc_reference layout.
That's it everyone!
Thanks for the patience and have a nice week!
Lucas Vaz