2021-12-28 05:29 AM
I have problem with the result from ADC. I divide it into two problems and will try to describe them good below. First some facts about the design, software and ADC configuration.
Device: STM32G0B1CCU6
Software using: STM32CubeG0 MCU Firmware Package V1.5.0
Config: HSE=24MHz SYSCLK=40MHz HCLK=APB=20MHz PLLQ=ToUSB=48MHz
SYSCLK=40MHz->ToADC(async) ADC=async/2=20MHz tS=3.5 tCONV=tS+12.5=16 fS=1.25MSps
25000 samples via DMA
PA3 connected to a light sensor, OPT101 via a RC filter (100ohm 1nF).
void HAL_MspInit(void)
{
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
HAL_SYSCFG_VREFBUF_VoltageScalingConfig(SYSCFG_VREFBUF_VOLTAGE_SCALE1); // Internal voltage reference 2.5V
HAL_SYSCFG_EnableVREFBUF();
HAL_SYSCFG_VREFBUF_HighImpedanceConfig(SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE);
HAL_SYSCFG_StrobeDBattpinsConfig(SYSCFG_CFGR1_UCPD1_STROBE | SYSCFG_CFGR1_UCPD2_STROBE);
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (hadc->Instance == ADC1) {
__HAL_RCC_ADC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_3; // PA3
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
hDmaAdc.Instance = DMA1_Channel1;
hDmaAdc.Init.Request = DMA_REQUEST_ADC1;
hDmaAdc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hDmaAdc.Init.PeriphInc = DMA_PINC_DISABLE;
hDmaAdc.Init.MemInc = DMA_MINC_ENABLE;
hDmaAdc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hDmaAdc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hDmaAdc.Init.Mode = DMA_NORMAL;
hDmaAdc.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hDmaAdc) != HAL_OK) {
Error_Handler();
}
__HAL_LINKDMA(hadc, DMA_Handle, hDmaAdc);
}
}
static void InitAdc(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hAdc.Instance = ADC1;
hAdc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; // ADC Clock Mux
hAdc.Init.Resolution = ADC_RESOLUTION_12B;
hAdc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hAdc.Init.ScanConvMode = ADC_SCAN_SEQ_FIXED;
hAdc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hAdc.Init.LowPowerAutoWait = DISABLE;
hAdc.Init.LowPowerAutoPowerOff = DISABLE;
hAdc.Init.ContinuousConvMode = ENABLE;
hAdc.Init.NbrOfConversion = 1;
hAdc.Init.DiscontinuousConvMode = DISABLE;
hAdc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hAdc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hAdc.Init.DMAContinuousRequests = DISABLE;
hAdc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hAdc.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_3CYCLES_5;
hAdc.Init.OversamplingMode = DISABLE;
hAdc.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hAdc) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hAdc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
#1 Slow rising
I early discover that the first sample seems to be lower than the others. However after looking at curves I noted that is was more than the first sample. Signal is slowly rising. I have made a workaround for this, by sample 1000 extra sample and discard them. The picture show with different serie resistor on the analog input. This is from prototype no 1. The second have different DC level (around 200).
#2 DC offset
It is some kind of DC offset. DC level measured with oscilloscope isn't that low from the ADC. I think that this mysterious DC offset level keep the ADC from see low level signal and therefor give curves like this:
The level is different betwen the two prototype boards I have and seems to differ if I change the RC filter in front of ADC input pin.
#3 Overshoot
It will for some level of signals be a heavy overshoot in the curves from the ADC. Note that the 50 Hz square wave signal to the ADC isn't straight square wave. It is bandwidth limited by the light sensor. So the upper corner to the left of the rising signal shouldn't be sharp, it will round off little like the curves down to right in the pictures. However overshoot and ringing as in these curves shouldn't exist. These cuvres are from prototype number 2 and the DC offset differ from previous curves above.
What have I done wrong?
//Michael
Solved! Go to Solution.
2021-12-28 06:34 AM
The sampling capacitor in AC is charged internally to some level (presumably half of VREF) and then connected to your signal for the sampling period. If your signal is high impedance, you will see this on the scope, and if the sampling period is too short, it will have influence on the output of ADC.
JW
2021-12-28 05:31 AM
More investigation of the DC offset. I remove the light sensor and instead mounted a resistor of 1kohm from PA3 to GND. This give a level close to zero, as can seen in picture below. I added a resistor to VDD and got a straight line at 2683-2696, which corresponds to 1.64 volt. It seems good, half of 3.3 volt.
Now I got an idea of measure with an oscilloscope at the analog input (with only a resistor to ground). It looks like this, while the ADC is working:
Where does this signal come from? Please note the high level (0.5 volt), at 1kohm load.
Is it this that destroy my measurements. Can it build up a DC level. Let us mount a capacitor over the resistor at PA3. The result can be seen in picture above at the third row of curves. Yes, a DC level is measured by the ADC.
How can I avoid this?
//Michael
2021-12-28 06:24 AM
> hAdc.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_3CYCLES_5;
The sensor you have has a very high output impedance. You should increase the sampling time to improve accuracy.
What is the hardware setup difference between "PA3 - 1kOhm - GND" which gives the expected result and "ULS P1 PA3 - 1kOhm - GND" which does not?
I would imagine most/all of these are artifacts from the sensor and not the ADC.
2021-12-28 06:34 AM
The sampling capacitor in AC is charged internally to some level (presumably half of VREF) and then connected to your signal for the sampling period. If your signal is high impedance, you will see this on the scope, and if the sampling period is too short, it will have influence on the output of ADC.
JW
2021-12-28 08:40 AM
Can you switch to a digital light sensor?
The OPT101 light sensor is an old device designed for resistive load to ground.
2021-12-28 11:01 AM
Big thanks for all answers.
"The sensor you have has a very high output impedance. You should increase the sampling time to improve accuracy."
I want the selected sample time (1.25MHz), it's a requirement.
Since the sensor includes an OP I did not think it have high output impedance. Now I see in datasheet it actually have problem to sink current. Therefor it also contain a JFET for that purpose. However it require a negative power supply and I don't have that.
"What is the hardware setup difference between "PA3 - 1kOhm - GND" which gives the expected result and "ULS P1 PA3 - 1kOhm - GND" which does not?"
They both have only a resistor to ground at the analog input pin. The difference is that the first is the values from the ADC and the second is a measurement with an oscilloscope.
"The sampling capacitor in ADC is charged internally to some level (presumably half of VREF) and then connected to your signal for the sampling period."
Thank you very much for this information. It explained why it behaves as it does.
"Can you switch to a digital light sensor?"
Yes, if I can find one that is fast enough and pretty cheap. I actually have plan to design an own light sensor. Little bit like the OPT101, but built with separate components. I found the "1 MHz, Single-Supply, Photodiode Amplifier Reference Design" document from Texas Instruments. I would like faster response than OPT101 and better light sensitivity.
"The OPT101 light sensor is an old device designed for resistive load to ground."
I have now read more in the datasheet and realize that OPT101 cannot sink current.
Could one solution be to add an OP between OPT101 and ADC (MCU)?
Should it be a resistor between OP output and the ADC input? That should comply with the Max.RAIN 680 ohm in datasheet. For example 100 ohm, or can the OP output handle the current from the ADC directly connected?
Can you recommend a cheap OP?
//Michael
2021-12-29 05:13 AM
I mounted an OP and got better results. Now low levels are visible and no overshoots. It looks like curves I'm used to see from this sensor, then it is connected to an oscilloscope. I connected the OP (MAX44259) as a buffer between the sensor and the ADC. Between OP output and ADC input is a resistor. I believe this resistor is needed to reduce the peak current at sample time. I mounted 470 ohm and think that is good, since datasheet specify max 680 ohm for the configuration I use. I already have 100 ohm resistors in the design and was on the they to select it for this position also. However I think it is better with higher value, to reduce the current. I will experiment with different value to see if it effects the sample values. I will also test to load the OPT101 output. Right now it only goes directly to the OP input.
THANKS!
//Michael
2021-12-30 01:12 AM
I found an application note that describe this and more about ADC:
AN2834 How to get the best ADC accuracy in STM32 microcontrollers
I recommend it to everyone that shall use the ADC in STM32.
2021-12-30 11:51 AM
That MCU has an ADC calibration feature. At least the code shown doesn't show the calibration code. The calibration should remove the DC offset.
2021-12-31 01:53 AM
I did a calibration before first measurement, after power up. However have now changed and always do it, before each measurement. The code sequence is like this:
HAL_ADCEx_Calibration_Start()
HAL_ADC_Start_DMA()
Wait for HAL_ADC_ConvCpltCallback()
HAL_ADC_Stop_DMA()
Send data on USB
Regarding the DC offset that can be seen in the curves. In the first picture it was caused by the current spikes from ADC, capacitor on input pin and the lack of sink capacity of the signal source. In the last picture above, the dc offset is actually from the source. The light sensor don't go down to zero. In one curve the sample values are 10 to 18, which correspond to 6.1 - 11 mV. The light sensor is specified to have 5 - 10 mV dc level at dark. So now all looks good to me. However I wan't a faster light sensor, so I will replace OPT101.