Hi,
In Xiao SAMD21, I designed a PCB to measure Voltage using Xiao SAMD21 internal ADC. But always getting Noise data details are attached below. In program
ADC set to 12 bit mode.
Using analogRead function.
Tried many ways to clear the noise, none of them are not working. Pls suggest a way to solve.
You must remember that when measuring a voltage up to 3.3V with 12 bit resolution means that a change of 1 in the reading from the adc is less than 1mV. That's a very small voltage! So it's very difficult to build a circuit that will not have this problem. If you are using a breadboard or flying wires, you will not be able to do it. Maybe if you design a custom PCB with great care and expertise, you can almost remove the noise.
One way to easily remove some of the noise is to take many readings from the adc and take the average. Another way is to put a small capacitor between the input pin and ground. Both of these ways will mean you cannot measure fast-changing signals.
I've heard some of the SAMD boards have inherent noise issues. I don't know from experience. Do you have a capacitor right at the analog in pin?
I read an article some time ago ( don't recall the platform) that suggested stopping some hardware functions that could be making noise (PWM, timers etc). I don't know if that would help here.
The Seeeduino Xiao SAMD21 board makes design compromises to obtain its small footprint. One of these compromises, is that the analog voltage supply (VDDANA) is contected directly to the digital supply (VDDIOx) pins and omits the ferrite bead filtering recommended by the SAMD21 datasheet.
The board also provides no possiblity of using an external precision voltage reference, since the AREF pin isn't broken out and easily accessible.
You could try switching the ADC to the microcontroller's internal 1V reference: AR_INTERNAL1V0, since this is derived from the stable on-chip bandgap voltage reference, but his means limiting your analog input range to between 0V and 1V.
You have provided this data but no indication I can see of the expected range of input voltage - nor even what signal your data represents.
Here is the data in a sensible graph
Your "signal" seems to change at about 300 and 850 in the graph.
If the signal is representative of what you want to measure you NEED to do some signal conditioning before attempting to measure it.
Before you even connect it to an ADC you need to decide the range and frequency characteristics of your signal and the accuracy and precision to which you need to measure it.
Read this:
If you don't require a super high sample rate, you could try accumulating the result over a number of samples then averaging, to reduce noise.
The code example below sets-up the ADC just like analogRead() on A0, however it's also configured to accumulate over 256 samples. This generates a 20-bit value which the ADC auto shifts to the right by 4 (divide by 16), giving a 16-bit result. Setting the AVGCTRL.ADJRES to 0x4, shifts the 16-bit value to the right by a further 4 bits, giving an averaged 12-bit result:
// Set-up the ADC to read analog input on A0 averaged over 256 samples
// The signal is accululated over 256 samples, auto shifted right by 4 generating a 16-bit value
// This value is then divided by 4 to create an averaged 12-bit result
void setup() {
SerialUSB.begin(115200); // Set-up the native serial port at 115200 bit/s
while(!SerialUSB); // Wait for the console to open
// Selecting the port multiplexer the the ADC doesn't seem necessary on the SAMD21
//PORT->Group[PORTA].PINCFG[2].bit.PMUXEN = 1; // Enable the A0 pin multiplexer
//PORT->Group[PORTA].PMUX[2 >> 1].reg |= PORT_PMUX_PMUXE_B; // Switch the multiplexer to the ADC input
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 | // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
ADC_CTRLB_RESSEL_16BIT; // Set ADC resolution to either 16-bit accumulation/averaging mode
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(0x4) | // Divide the accumulated 16-bit value by 4 generating an averaged 12-bit result
ADC_AVGCTRL_SAMPLENUM_256; // Accumulates 256 samples generating 16-bit value
ADC->SAMPCTRL.reg = 0x3f; // Set max Sampling Time Length to half divided ADC clock pulse times SAMPCTRL (341.33us)
ADC->INPUTCTRL.bit.MUXPOS = 0x0; // Set the analog input to A0
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->CTRLA.bit.ENABLE = 1; // Enable the ADC
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
}
void loop() {
ADC->SWTRIG.bit.START = 1; // Initiate a software trigger to start an ADC conversion
while(ADC->STATUS.bit.SYNCBUSY); // Wait for write synchronization
while(!ADC->INTFLAG.bit.RESRDY); // Wait for the conversion to complete
ADC->INTFLAG.bit.RESRDY = 1; // Clear the result ready (RESRDY) interrupt flag
while(ADC->STATUS.bit.SYNCBUSY); // Wait for read synchronization
int result = ADC->RESULT.reg; // Read the ADC result
SerialUSB.println(result); // Output the result
delay(500); // Wait for 500ms
}
The SAMD21's datasheet gives more information on how to adjust these register values.
What is the purpose of this measurement?
Can we see a photo of your setup please? The "noise" may be coming from somewhere else.
Are there any other connections?
Are you keeping analog, digital and load grounds seperate?
What is the battery being used for?
The code above sacrifices sample rate for stability with the averaged sample time being 87ms or so.
The code example above will block, waiting for the ADC to complete.
The code example below however uses the ADC's interrupts to implement a non-blocking method, where the loop() function doesn't wait for the results:
// Set-up the ADC to read analog input on A0 averaged over 256 samples, non-blocking
// The signal is accululated over 256 samples, auto shifted right by 4 generating a 16-bit value
// This value is then divided by 4 to create an averaged 12-bit result
volatile uint16_t adcResult; // ADC result
volatile bool resultsReady = false; // Results ready flag
void setup() {
SerialUSB.begin(115200); // Set-up the native serial port at 115200 bit/s
while(!SerialUSB); // Wait for the console to open
// Selecting the port multiplexer the the ADC doesn't seem necessary on the SAMD21
//PORT->Group[PORTA].PINCFG[2].bit.PMUXEN = 1; // Enable the A0 pin multiplexer
//PORT->Group[PORTA].PMUX[2 >> 1].reg |= PORT_PMUX_PMUXE_B; // Switch the multiplexer to the ADC input
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 | // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
ADC_CTRLB_RESSEL_16BIT; // Set ADC resolution to either 16-bit accumulation/averaging mode
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(0x4) | // Divide the accumulated 16-bit value by 4 generating an averaged 12-bit result
ADC_AVGCTRL_SAMPLENUM_256; // Accumulates 256 samples generating 16-bit value
ADC->SAMPCTRL.reg = 0x3f; // Set max Sampling Time Length to half divided ADC clock pulse times SAMPCTRL (341.33us)
ADC->INPUTCTRL.bit.MUXPOS = 0x0; // Set the analog input to A0
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
NVIC_SetPriority(ADC_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for the ADC to 0 (highest)
NVIC_EnableIRQ(ADC_IRQn); // Connect the ADC to Nested Vector Interrupt Controller (NVIC)
ADC->INTENSET.reg = ADC_INTENSET_RESRDY; // Generate interrupt on result ready (RESRDY)
ADC->CTRLA.bit.ENABLE = 1; // Enable the ADC
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->SWTRIG.bit.START = 1; // Initiate a software trigger to start an ADC conversion
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
}
void loop()
{
if (resultsReady) // Check if the results are ready
{
SerialUSB.println(adcResult); // Display the results
resultsReady = false; // Clear the resultsReady flag
ADC->SWTRIG.bit.START = 1; // Initiate a software trigger to start an ADC conversion
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
}
}
void ADC_Handler()
{
if (ADC->INTFLAG.bit.RESRDY) // Check if the result ready (RESRDY) flag has been set
{
ADC->INTFLAG.bit.RESRDY = 1; // Clear the RESRDY flag
while(ADC->STATUS.bit.SYNCBUSY); // Wait for read synchronization
adcResult = ADC->RESULT.reg; // Read the result;
resultsReady = true; // Set the resultsReady flag
}
}
Hi all,
i have exact the same problem but using more than 1 pin for measurement.
I designed a PCB circuit and tested on breadboard and was hoping the noise could be excluded by the final design ( actually received first maufactured sample) and additional via the SAMD_AnalogCorrection.h (only offset).
I already implemented an array for multiple analogread() averaging but no nice result to me.
So this solution will be the right one for me.
I also have some interrupts for fast freq measurement added on pin A1.
But how can i use several pins as analog input with the XIAO?
I am using the pins A2,A9,A10 for permanent measurements (no sinus) .
The A7 and A8 also used as AI but only for the AnalogCorrection which is done before the final software is loaded.
How can i use the obove mentioned code with the 3 pins (A2,A9,A10)?