2022-09-22 11:51 AM
Hi,
I'm seeing some very strange behavior on the ADC of the BlueNRG-LP. It looks like the ADC is skipping codes near the inversion point of a differential input, with similar behavior in single ended mode near 0.9V (with a 1.2V reference). This is on the STEVAL-IDB011V2 development kit.
High level description of the ADC configuration:
* Differential mode input on PB2/PB3 (differential input 0)
* 1MSPS sample rate
* 16 bit depth
* Downsampler active (ratio varies)
* Input common mode at 0.6V
* 1.2V internal reference voltage
* Analog watchdog off
* DMA active
What I'm seeing is that the ADC skips over a range close to, but not exactly at, the midpoint of the sample range. In 16 bit mode the range is approx 32700-32750 (with 32768 being the midpoint). See attached images (X axis is seconds, Y axis ADC code).
This is with the downsampler active.
And this is sampling at full 1MSPS, no downsampling. Data is left shifted but you can clearly see that 1 or 2 codes are being skipped. This is a 1kHz sine wave input.I see the same behavior in single-ended mode around 0.9V input, which is even further from the midpoint (which is 0.6V with a 1.2V reference). I've seen the same behavior on another analog input as well (PA14/PA15 as differential input).
Here is the code being used to configure the ADC.
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
/* Parameters for ADC initialization */
__HAL_RCC_ADCDIG_CLK_ENABLE();
__HAL_RCC_ADCANA_CLK_ENABLE();
#if USE_PA_FOR_ANALOG
LL_AHB_EnableClock(LL_AHB_PERIPH_GPIOA);
GPIO_TypeDef* port_pos = GPIOA;
uint32_t pin_pos = LL_GPIO_PIN_15;
GPIO_TypeDef* port_neg = GPIOA;
uint32_t pin_neg = LL_GPIO_PIN_14;
#else
LL_AHB_EnableClock(LL_AHB_PERIPH_GPIOB);
GPIO_TypeDef* port_pos = GPIOB;
uint32_t pin_pos = LL_GPIO_PIN_3;
GPIO_TypeDef* port_neg = GPIOB;
uint32_t pin_neg = LL_GPIO_PIN_2;
#endif
/* Configure ADC PINs */
LL_GPIO_SetAFPin_0_7( port_pos, pin_pos, LL_GPIO_AF_0);
LL_GPIO_SetPinMode( port_pos, pin_pos, LL_GPIO_MODE_ANALOG);
LL_GPIO_SetPinSpeed( port_pos, pin_pos, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetPinOutputType(port_pos, pin_pos, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull( port_pos, pin_pos, LL_GPIO_PULL_NO);
LL_GPIO_SetAFPin_0_7( port_neg, pin_neg, LL_GPIO_AF_0);
LL_GPIO_SetPinMode( port_neg, pin_neg, LL_GPIO_MODE_ANALOG);
LL_GPIO_SetPinSpeed( port_neg, pin_neg, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetPinOutputType(port_neg, pin_neg, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull( port_neg, pin_neg, LL_GPIO_PULL_NO);
}
/* @brief ADC Initialization Function
* @param None
* @retval None
*/
static void MX_ADC_Init(uint32_t ds_ratio)
{
/* Enable the ADC peripheral */
HAL_ADC_StructInit(&adc_handle);
adc_handle.Init.DataRatio = ds_ratio;
adc_handle.Init.DataWidth = USER_DATAWIDTH;
adc_handle.Init.SampleRate = USER_SAMPLERATE;
#if defined(CONFIG_DEVICE_BLUENRG_LPS)
adc_handle.Init.SampleRateMsb = USER_SAMPLERATE_MSB;
#endif
adc_handle.DMA_Handle = &hdma_adc;
if (HAL_ADC_Init(&adc_handle) != HAL_OK) {
Error_Handler();
}
SYSCFG->GPIO_SWA_CTRL |= 2;
LL_ADC_ConfigureMicrophonePGA(adc_handle.Instance, LL_ADC_PGA_BIAS_090_BAT, 0);
/* DMA controller clock enable */
__HAL_RCC_DMA_CLK_ENABLE();
hdma_adc.Instance = DMA1_Channel1;
hdma_adc.Init.Request = DMA_REQUEST_ADC_DS;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc.Init.Mode = dma_mode_;
hdma_adc.Init.Priority = DMA_PRIORITY_LOW;
if(HAL_DMA_Init(&hdma_adc) != HAL_OK) {
Error_Handler();
}
__HAL_LINKDMA(&adc_handle, DMA_Handle, hdma_adc);
/* Start the sampling at end of previous sample */
LL_ADC_InputSamplingMode(ADC, LL_ADC_SAMPLING_AT_START);//LL_ADC_SAMPLING_AT_END);
/* Set the input channel */
#if USE_PA_FOR_ANALOG
xChannel.ChannelType = ADC_CH_VINP2_VINM2_TO_DIFF_INPUT;
#else
xChannel.ChannelType = ADC_CH_VINP0_VINM0_TO_DIFF_INPUT;
#endif
//xChannel.ChannelType = ADC_CH_VINP0_TO_SINGLE_POSITIVE_INPUT;
//xChannel.ChannelType = ADC_CH_VINM0_TO_SINGLE_NEGATIVE_INPUT;
xChannel.SequenceNumber = ADC_SEQ_POS_01;
#if 1
xChannel.VoltRange = ADC_VIN_RANGE_1V2;
//xChannel.VoltRange = ADC_VIN_RANGE_2V4;
if (HAL_ADC_ConfigChannel(&adc_handle, &xChannel)!= HAL_OK) {
Error_Handler();
}
if(LL_ADC_GET_CALIB_GAIN_FOR_VINDIFF_1V2() != 0xFFF) {
LL_ADC_SetCalibPoint1Gain(ADC, LL_ADC_GET_CALIB_GAIN_FOR_VINDIFF_1V2() );
offset_vinp0 = LL_ADC_GET_CALIB_OFFSET_FOR_VINDIFF_1V2();
#ifdef CONFIG_DEVICE_BLUENRG_LP
if(offset_vinp0 < -64 || offset_vinp0 > 63) {
// Intrinsic offset too large to compensate!
asm("bkpt 0");
}
#endif
LL_ADC_SetCalibPoint1Offset(ADC, offset_vinp0);
offset_vinp0 = 0;
}
else {
LL_ADC_SetCalibPoint1Gain(ADC, LL_ADC_DEFAULT_RANGE_VALUE_1V2);
}
LL_ADC_SetCalibPointForDiff1V2(ADC, LL_ADC_CALIB_POINT_1);
#else
// experimental: Try the 2.4V reference range.
xChannel.VoltRange = ADC_VIN_RANGE_2V4;
if (HAL_ADC_ConfigChannel(&adc_handle, &xChannel)!= HAL_OK) {
Error_Handler();
}
if(LL_ADC_GET_CALIB_GAIN_FOR_VINDIFF_2V4() != 0xFFF) {
LL_ADC_SetCalibPoint1Gain(ADC, LL_ADC_GET_CALIB_GAIN_FOR_VINDIFF_2V4() );
offset_vinp0 = LL_ADC_GET_CALIB_OFFSET_FOR_VINDIFF_2V4();
#ifdef CONFIG_DEVICE_BLUENRG_LP
if(offset_vinp0 < -64 || offset_vinp0 > 63) {
// Intrinsic offset too large to compensate!
asm("bkpt 0");
}
#endif
LL_ADC_SetCalibPoint1Offset(ADC, offset_vinp0);
offset_vinp0 = 0;
}
else {
LL_ADC_SetCalibPoint1Gain(ADC, LL_ADC_DEFAULT_RANGE_VALUE_2V4);
}
LL_ADC_SetCalibPointForDiff2V4(ADC, LL_ADC_CALIB_POINT_1);
#endif
}
Any ideas? Thank you.