ADC using DMA
I'm trying to use the LL interface to read 2 ADC channels (0 & 1) using DMA. They're configured pretty much as default, 12-bit samples into 16-bit memory variables. I don't see any errors. The transfer completes, as does the EOS. There's data in the DR. However only a single byte makes it into memory. (There should be 4 bytes.)
Any thoughts on where the misconfiguration might be?
adc.c generated code
void MX_ADC1_Init(void)
{
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
LL_ADC_InitTypeDef ADC_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_GPIOA);
/**ADC1 GPIO Configuration
PA0 ------> ADC1_IN0
PA1 ------> ADC1_IN1
*/
GPIO_InitStruct.Pin = TUBE_I_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(TUBE_I_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = TUBE_HV_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(TUBE_HV_GPIO_Port, &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_LOW);
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);
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS;
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);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_3CYCLES_5);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_3CYCLES_5);
LL_ADC_DisableIT_EOC(ADC1);
LL_ADC_DisableIT_EOS(ADC1);
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);
/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_COMMON_1);
/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_COMMON_1);
}My code:
static void configureDma(void)
{
// Select ADC as DMA transfer request.
LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_0, LL_DMAMUX_REQ_ADC1);
// DMA transfer addresses and size.
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,
LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
(uint32_t)rawAdc,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC1_NUM_CHANNELS);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); // Enable transfer complete interrupt.
LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); // Enable half transfer interrupt.
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); // Enable transfer error interrupt.
// Enable
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
}
static void activateAdc(void)
{
__IO uint32_t wait_loop_index = 0;
/* 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. */
wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
while(wait_loop_index != 0)
{
wait_loop_index--;
}
// DMA must be disabled for calibration.
const uint32_t dma_tx_mode = LL_ADC_REG_GetDMATransfer(ADC1);
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);
/* Run ADC self calibration */
LL_ADC_StartCalibration(ADC1);
/* Poll for ADC effectively calibrated */
while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0)
{
}
/* Delay between ADC end of calibration and ADC enable. */
/* Note: Variable divided by 2 to compensate partially */
/* CPU processing cycles (depends on compilation optimization). */
wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);
while(wait_loop_index != 0)
{
wait_loop_index--;
}
// Re-enable DMA.
LL_ADC_REG_SetDMATransfer(ADC1, dma_tx_mode);
/* Enable ADCs */
LL_ADC_Enable(ADC1);
/* Poll for ADCs ready to convert */
while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0)
{
}
}
void AdcGetValues(uint32_t * current_uA, uint32_t * voltage_V)
{
rawAdc[0] = 0x1234;
rawAdc[1] = 0x4321;
/* Clear ADC End of Conversion Sequence flag. */
LL_ADC_ClearFlag_EOS(ADC1);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC1_NUM_CHANNELS);
/* Start conversion. */
LL_ADC_REG_StartConversion(ADC1);
/* Wait for completion. */
while (!LL_ADC_IsActiveFlag_EOS(ADC1))
;
/* Convert the data. */
*current_uA = (((uint32_t)rawAdc[0] * MAX_I_UA) + (MAX_ADC / 2U)) / MAX_ADC;
*voltage_V = (((uint32_t)rawAdc[1] * MAX_V_V) + (MAX_ADC / 2U)) / MAX_ADC;
}Interrupt handler:
void DMA1_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
/* Check whether DMA transfer complete caused the DMA interruption */
if(LL_DMA_IsActiveFlag_TC1(DMA1) == 1)
{
/* Clear flag DMA transfer complete */
LL_DMA_ClearFlag_TC1(DMA1);
/* Call interruption treatment function */
}
/* Check whether DMA half transfer caused the DMA interruption */
if(LL_DMA_IsActiveFlag_HT1(DMA1) == 1)
{
/* Clear flag DMA half transfer */
LL_DMA_ClearFlag_HT1(DMA1);
/* Call interruption treatment function */
}
/* Note: If DMA half transfer is not used, possibility to replace */
/* management of DMA half transfer and transfer complete flags by */
/* DMA global interrupt flag: */
/* Clear flag DMA global interrupt */
/* (global interrupt flag: half transfer and transfer complete flags) */
// LL_DMA_ClearFlag_GI1(DMA1);
/* Check whether DMA transfer error caused the DMA interruption */
if(LL_DMA_IsActiveFlag_TE1(DMA1) == 1)
{
/* Clear flag DMA transfer error */
LL_DMA_ClearFlag_TE1(DMA1);
/* Call interruption treatment function */
}
/* USER CODE END DMA1_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
/* USER CODE END DMA1_Channel1_IRQn 1 */
}