2022-07-26 07:50 AM
STM32G070R8T6
STM32CubeIDE Version: 1.8.0
We are bringing up our new board and using ADC1 to DMA PB0, PB1, PB2 and PB10 pin readings into memory. The actual level on the pins is ~1.358 volts and the DMA data from the pins seen by software are 1775 to 1786. The ADC is set to 12 bits so these readings are showing 1.43 to 1.439 volts. This is an error of 72mVolts which seems excessive.
The initial code by Cube was set to use Low Level drivers calls.The ADC is set to use 4 ranks and 19 cycle samples. The Cube code also has run a ADC calibrate so we are at a loss to pinpoint why the four ADC pin readings are so far off.
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
LL_ADC_InitTypeDef ADC_InitStruct = {0};
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
/**ADC1 GPIO Configuration
PB0 ------> ADC1_IN8
PB1 ------> ADC1_IN9
PB2 ------> ADC1_IN10
PB10 ------> ADC1_IN11
*/
GPIO_InitStruct.Pin = SW12_ANALOG_PB0 | SW34_ANALOG_PB1 | SW56_ANALOG_PB2 | SW78_ANALOG_PB10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
/* USER CODE BEGIN ADC1_Init 1 */
/* Select ADC as DMA transfer request */
LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_0, LL_DMAMUX_REQ_ADC1);
/* Set DMA transfer addresses of source and destination */
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
(uint32_t)&dmaBuff, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
/* Set DMA transfer size */
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_CONVERTED_DATA_BUFFER_SIZE);
/* Enable DMA transfer interruption: transfer complete */
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
/* Enable DMA transfer interruption: half transfer */
LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
/* Enable DMA transfer interruption: transfer error */
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
/*## Activation of DMA #####################################################*/
/* Enable the DMA transfer */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
/* Configure NVIC to enable ADC1 interruptions */
NVIC_SetPriority(ADC1_IRQn, 0); /* ADC IRQ greater priority than DMA IRQ */
NVIC_EnableIRQ(ADC1_IRQn);
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
#define ADC_CHANNEL_CONF_RDY_TIMEOUT_MS (1U)
#if (USE_TIMEOUT == 1)
uint32_t Timeout ; /* Variable used for Timeout management */
#endif /* USE_TIMEOUT */
ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
LL_ADC_Init(ADC1, &ADC_InitStruct);
LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_FIXED);
LL_ADC_REG_SetSequencerChannels(ADC1, (ADC_CHSELR_CHSEL8_Msk | ADC_CHSELR_CHSEL9_Msk | ADC_CHSELR_CHSEL10_Msk | ADC_CHSELR_CHSEL11_Msk));
// For Set the sequence
/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS;
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
// Scan low channel to high channel(0-->18: 8, 9, 10 and 11)
LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_19CYCLES_5);
LL_ADC_DisableIT_EOC(ADC1);
LL_ADC_DisableIT_EOS(ADC1);
/* Enable ADC internal voltage regulator */
LL_ADC_EnableInternalRegulator(ADC1);
/* Delay for ADC internal voltage regulator stabilization. */
/* Compute number of CPU cycles to wait for, from delay in us. */
/* Note: Variable divided by 2 to compensate partially */
/* CPU processing cycles (depends on compilation optimization). */
/* Note: If system core clock frequency is below 200kHz, wait time */
/* is only a few CPU processing cycles. */
uint32_t wait_loop_index;
wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
while(wait_loop_index != 0)
{
wait_loop_index--;
}
LL_ADC_REG_SetSequencerChannels(ADC1, (ADC_CHSELR_CHSEL8_Msk | ADC_CHSELR_CHSEL9_Msk | ADC_CHSELR_CHSEL10_Msk | ADC_CHSELR_CHSEL11_Msk));
/** Configure Regular Channel
*/
// PB2: pin 29, ADC_IN10
// PB10: pin 30, ADC_IN11
// PB11: pin 31, ADC_IN15
// PB12: pin 32, ADC_IN16
//LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_10);
//LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_11);
//LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_15);
//LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_16);
/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
//LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_COMMON_1);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
/** Configure Regular Channel
*/
//LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4);
LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_0 | LL_ADC_CHANNEL_1 | LL_ADC_CHANNEL_2| LL_ADC_CHANNEL_10);
/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
//LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_COMMON_1);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
/* USER CODE BEGIN ADC1_Init 2 */
/* Configuration of ADC interruptions */
/* Enable interruption ADC group regular overrun */
LL_ADC_EnableIT_OVR(ADC1);
LL_ADC_EnableIT_EOC(ADC1);
LL_ADC_EnableIT_EOS(ADC1);
LL_ADC_EnableIT_EOSMP(ADC1);
LL_ADC_Enable(ADC1);
/* USER CODE END ADC1_Init 2 */
}
Solved! Go to Solution.
2022-07-28 07:19 AM
DMA issue was the incorrect channels used in LL_ADC_REG_SetSequencerChAdd() call. After hardware modifications the pinouts changed and we modified the LL_ADC_REG_SetSequencerChannels() call but missed the LL_ADC_REG_SetSequencerChAdd().
LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_8 | LL_ADC_CHANNEL_9 | LL_ADC_CHANNEL_10| LL_ADC_CHANNEL_11);
DMA buffer to ADC channel alignment is fixed.
2022-07-28 07:55 AM
ADC issue appears to be noise on the line during sampling. All original ADC sample time settings were for 19 clock cycles(LL_ADC_SAMPLINGTIME_19CYCLES_5.) After changing the sampling down to 3 clock cycles(LL_ADC_SAMPLINGTIME_3CYCLES_5) the sample levels dropped:
The 1720 minimum value of PB1 is 1.386 volts, which is very close to the expected 1.358 volts. We do see a small amount of noise on those pins which must be driving the ADC level up during a 19 clock cycle sample.
I gave credit to the "Borg" avatar member for pointing out sample and hold time settings. Thank you for all the comments/suggestions in resolving this issue.