cancel
Showing results for 
Search instead for 
Did you mean: 

Can i use DMA-ADC1 for continous conversion and ADC2 for single conversion at the same time?

FArza.1
Associate II

Hello there, I've been looking a way to sample ADC continously with DMA for measuring power supply current and voltage, and then another ADC for temperature reading which probably just readed only once per second.

I've read STM32 ADC modes and yet don't understand whether my application was possible, probably because my english was not that good.

I have tried some code by myself, but can't get it to work

Here is my code :

extern "C" void DMA1_Channel1_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_adc1);
}
 
extern "C" void HAL_ADC_MspInit(ADC_HandleTypeDef* hadcx)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  if(hadcx->Instance==ADC2){
    __HAL_RCC_ADC2_CLK_ENABLE();
  }
  
  if(hadcx->Instance==ADC1)
  {
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hadcx,DMA_Handle,hdma_adc1);
  }
 
}
 
 
extern "C" void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadcx)
{
 
  if(hadcx->Instance==ADC1)
  {
    __HAL_RCC_ADC1_CLK_DISABLE();
    HAL_DMA_DeInit(hadcx->DMA_Handle);
  }
  
  if(hadcx->Instance==ADC2)
  {
    __HAL_RCC_ADC2_CLK_DISABLE();
    HAL_DMA_DeInit(hadcx->DMA_Handle);
  }
 
}
 
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
 
 
static void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
  ADC_ChannelConfTypeDef sConfig2 = {0};
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Common config 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 4;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  
  /*
  sConfig2.Channel = ADC_CHANNEL_8;
  sConfig2.Rank = ADC_REGULAR_RANK_3;
  sConfig2.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig2) != HAL_OK){
    Error_Handler();
  }
  */
  //////////////////////////////////////////////////////////////////
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
    Error_Handler();
  }
 
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
    Error_Handler();
  }
 
  sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = ADC_REGULAR_RANK_4;
  sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
    Error_Handler();
  }
 
 
  if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK){
    Error_Handler();
  }
 
  hadc2.Instance = ADC2;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig2.Channel = ADC_CHANNEL_6;
  sConfig2.Rank = ADC_REGULAR_RANK_1;
  sConfig2.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig2) != HAL_OK){
    Error_Handler();
  }
  sConfig2.Channel = ADC_CHANNEL_7;
  sConfig2.Rank = ADC_REGULAR_RANK_2;
  sConfig2.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig2) != HAL_OK){
    Error_Handler();
  }
  //if (HAL_ADCEx_Calibration_Start(&hadc2) != HAL_OK){
    //Error_Handler();
  //}
}
 
int readTherm(int channel){
  ADC_ChannelConfTypeDef adcChannel = {0};
  switch(channel){
    case 0:
      //adcChannel.Channel = ADC_CHANNEL_8;
      break;
    case 1:
      adcChannel.Channel = ADC_CHANNEL_6;
      break;
    case 2:
      adcChannel.Channel = ADC_CHANNEL_7;
      break;
  }
  adcChannel.Rank = 1;
  adcChannel.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; //or any other value available.
  if(HAL_ADC_ConfigChannel(&hadc2, &adcChannel) != HAL_OK){
    Error_Handler();
  }
  HAL_ADC_Start(&hadc2);
  if (HAL_ADC_PollForConversion(&hadc2, 10) == HAL_OK){
    return HAL_ADC_GetValue(&hadc2);
  }
}
 
int raw_ch1Voltage;
int raw_ch2Voltage;
int raw_ch1Current;
int raw_ch2Current;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  
  if(AdcHandle->Instance==ADC1){
 
    raw_ch1Voltage = (dmaAdcBuffer[0]+dmaAdcBuffer[4]+dmaAdcBuffer[8])/3;
    raw_ch2Voltage = (dmaAdcBuffer[2]+dmaAdcBuffer[6]+dmaAdcBuffer[10])/3;
    raw_ch1Current = (dmaAdcBuffer[3]+dmaAdcBuffer[7]+dmaAdcBuffer[11])/3;
    raw_ch2Current = (dmaAdcBuffer[1]+dmaAdcBuffer[5]+dmaAdcBuffer[9])/3;
    // Add value from DMA buffer to running average (for smooth performance).
    ch1Voltage.reading(raw_ch1Voltage);
    ch2Current.reading(raw_ch2Current);
    ch2Voltage.reading(raw_ch2Voltage);
    ch1Current.reading(raw_ch1Current);
    
  }
}
 
void initializeHAL(){
  HAL_Init();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  HAL_ADC_Start(&hadc1);
  HAL_ADC_Start_DMA(&hadc1, dmaAdcBuffer, 12);
}

If i call HAL_ADC_Start(&hadc2) or readTherm(); or HAL_ADCEx_Calibration_Start(&hadc2); at some point on my code, the DMA crashed, the buffer was filled with some random big number, but the temperature reading is jsut fine.

the DMA could only able to work if i'm not calling those 3 function.

my chip is STM32F103C8T6

1 ACCEPTED SOLUTION

Accepted Solutions
Uwe Bonnes
Principal II

I do it without HAL

void Stm32l45Adc1Init(void)

{

   /* For L41, System clock is always selected.*/

   RCC->AHB2ENR |= RCC_AHB2ENR_ADCEN;

   RCC->AHB2RSTR |= RCC_AHB2RSTR_ADCRST;

   RCC->AHB2RSTR &= ~RCC_AHB2RSTR_ADCRST;

   /* Wake up from deep power down.*/

   ADC1->CR = 0;

   /* Remove Injected Queue disabled.*/

   ADC1->CFGR = 0;

   ADC1->CR |= ADC_CR_ADVREGEN;

   /* Wake up TS and VREF sensors and use HCLK/1 */

   ADC12_COMMON->CCR = (0 << ADC_CCR_PRESC_Pos) | ADC_CCR_TSEN |

       ADC_CCR_VREFEN;

   NutMicroDelay(20);

   /* Calibrate in single ended mode */

   ADC1->CR |= ADC_CR_ADCAL;

   while(ADC1->CR & ADC_CR_ADCAL)

       __NOP();

   /* Wake up all sensors and use HCLK/2 */

   ADC12_COMMON->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN | ADC_CCR_VBATEN;

   while((ADC1->ISR & ADC_ISR_ADRDY) == 0) {

       ADC1->CR |= ADC_CR_ADEN;

   }

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   /* Select channel 17 (temperature sensor) as first conversion*/

   ADC1->SQR1 |= (17 << ADC_SQR1_SQ1_Pos);

   /* Select longest sampling time for all channels. */

   ADC1->SMPR1 = 0x3ffffff8;

   ADC1->SMPR2 = 0x07ffffff;

   uint32_t jsqr;

   jsqr = 0 << ADC_JSQR_JSQ1_Pos; /* VTS,    1rst injected on IN17*/

    /* VMON, 2nd injected on IN16/9*/

   jsqr |= ((rev == 1) ? 16 : 9) << ADC_JSQR_JSQ2_Pos;

   jsqr |= 1 << ADC_JSQR_JL_Pos;  /* 2 injected channels*/

   NutSleep(1);

   ADC1->JSQR = jsqr;

   /* Allow auto injected channels.*/

   ADC1->CFGR = ADC_CFGR_JAUTO;

   /* Calculate slope of temperature sensor */

   v_cal30          = *(uint16_t*)0x1fff75a8;

   uint16_t v_cal130 = *(uint16_t*)0x1fff75ca;

   v_refint         = *(uint16_t*)0x1fff75aa;

   /* We measure at 3.0 Volt versus 3.3V for calibration.

    * Use 3.3/3.0 as correction factor */

   tk = (130.0 - 30.0) / (v_cal130 - v_cal30);

   NutSleep(1);

   while (ADC1->JSQR != jsqr) {

       printf("Failure writing JSQR: %08lx\n", ADC1->JSQR);

       NutSleep(500);

       ADC1->JSQR = jsqr;

   }

   /* Start conversion */

   ADC1->CR |= ADC_CR_ADSTART;

}

THREAD(Adc2Init, arg)

{

   HANDLE *data_ready = (HANDLE *) arg;

   HANDLE                adc_ready = 0;

   /* Wake up from deep power down.*/

   ADC2->CR = 0;

   /* Remove Injected Queue disabled.*/

   ADC2->CFGR = 0;

   ADC2->CR |= ADC_CR_ADVREGEN;

   NutMicroDelay(20);

   /* Calibrate in single ended mode */

   ADC2->CR |= ADC_CR_ADCAL;

   while(ADC2->CR & ADC_CR_ADCAL)

       __NOP();

   /* Calibrate in differential Mode */

   ADC2->CR |= ADC_CR_ADCALDIF;

   ADC2->CR |= ADC_CR_ADCAL;

   while(ADC2->CR & ADC_CR_ADCAL)

       __NOP();

   ADC2->CR = ADC_CR_ADVREGEN;

   if (Stm32DmaGet(&dma_info_adc2, dma_adc2_list)) {

       printf("Stm32DmaGet failed\n");

   }

   if (Stm32DmaRegisterHandler(&dma_info_adc2, adc2_dma_irq, &adc_ready)) {

       printf("Stm32DmaRegisterHandler failed\n");

   }

   Stm32DmaSetup(&dma_info_adc2, adc2_sample_data, (void*)&ADC2->DR,

             ADC_ION_SAMPLES * 2,

             0 * DMA_CCR_PL_0   | /* Low Priority */

             1 * DMA_CCR_PSIZE_0 | /* 16 bit peripheral size */

             1 * DMA_CCR_MSIZE_0 | /* 16 bit memory size */

             DMA_CCR_MINC       |

             DMA_CCR_CIRC       | /* Circular mode */

             DMA_CCR_HTIE       | /* Half transfer interrupt */

             DMA_CCR_TCIE );      /* Full transfer interrupt*/

   /* Get DMA running */

   Stm32DmaEnable(&dma_info_adc2);

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   ADC2->CFGR =

       ADC_CFGR_CONT  | /* Continous mode */

       ADC_CFGR_DMACFG | /* Circular DMA*/

       ADC_CFGR_DMAEN;  /* Enable DMA*/

   ADC2->CR |= ADC_CR_ADEN;

   while((ADC2->ISR & ADC_ISR_ADRDY) == 0) {

       __NOP();

   }

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   /* Select channel 11 (PA6/7) as single conversion*/

   /* Channel 11 differential und als einziger Kanal*/

   ADC2->DIFSEL = ADC_DIFSEL_DIFSEL_11;

   ADC2->SQR1 |= (11 << ADC_SQR1_SQ1_Pos);

   /* Select longest sampling time for all channels. */

   ADC2->SMPR1 = 0x3ffffff8;

   ADC2->SMPR2 = 0x07ffffff;

   NutSleep(1);

   /* Enable 16 times oversampling, no shift */

   ADC2->CFGR2 = ADC_CFGR2_ROVSE | (3 * ADC_CFGR2_OVSR_0) |

       (0 * ADC_CFGR2_OVSS_0);

   ADC2->CR |= ADC_CR_ADSTART;

   while(1) {

       NutEventWait(&adc_ready, NUT_WAIT_INFINITE);

       uint32_t uint_sum = 0;

       uint32_t *buf = adc2_sample_data[buffer_index];

       for (int i = 0; i < ADC_ION_SAMPLES / 2; i++) {

           uint32_t data = *buf++;

           uint_sum += (data >> 16) + (data & 0xffff);

       }

       /* Remove Offset to get signed number */

       ion_sum = uint_sum - 0x8000 * ADC_ION_SAMPLES;

       NutEventPost(data_ready);

   }

}

View solution in original post

5 REPLIES 5
Uwe Bonnes
Principal II

I am doing just that on a L412

FArza.1
Associate II

Can you show me how to do it?

Uwe Bonnes
Principal II

I do it without HAL

void Stm32l45Adc1Init(void)

{

   /* For L41, System clock is always selected.*/

   RCC->AHB2ENR |= RCC_AHB2ENR_ADCEN;

   RCC->AHB2RSTR |= RCC_AHB2RSTR_ADCRST;

   RCC->AHB2RSTR &= ~RCC_AHB2RSTR_ADCRST;

   /* Wake up from deep power down.*/

   ADC1->CR = 0;

   /* Remove Injected Queue disabled.*/

   ADC1->CFGR = 0;

   ADC1->CR |= ADC_CR_ADVREGEN;

   /* Wake up TS and VREF sensors and use HCLK/1 */

   ADC12_COMMON->CCR = (0 << ADC_CCR_PRESC_Pos) | ADC_CCR_TSEN |

       ADC_CCR_VREFEN;

   NutMicroDelay(20);

   /* Calibrate in single ended mode */

   ADC1->CR |= ADC_CR_ADCAL;

   while(ADC1->CR & ADC_CR_ADCAL)

       __NOP();

   /* Wake up all sensors and use HCLK/2 */

   ADC12_COMMON->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN | ADC_CCR_VBATEN;

   while((ADC1->ISR & ADC_ISR_ADRDY) == 0) {

       ADC1->CR |= ADC_CR_ADEN;

   }

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   /* Select channel 17 (temperature sensor) as first conversion*/

   ADC1->SQR1 |= (17 << ADC_SQR1_SQ1_Pos);

   /* Select longest sampling time for all channels. */

   ADC1->SMPR1 = 0x3ffffff8;

   ADC1->SMPR2 = 0x07ffffff;

   uint32_t jsqr;

   jsqr = 0 << ADC_JSQR_JSQ1_Pos; /* VTS,    1rst injected on IN17*/

    /* VMON, 2nd injected on IN16/9*/

   jsqr |= ((rev == 1) ? 16 : 9) << ADC_JSQR_JSQ2_Pos;

   jsqr |= 1 << ADC_JSQR_JL_Pos;  /* 2 injected channels*/

   NutSleep(1);

   ADC1->JSQR = jsqr;

   /* Allow auto injected channels.*/

   ADC1->CFGR = ADC_CFGR_JAUTO;

   /* Calculate slope of temperature sensor */

   v_cal30          = *(uint16_t*)0x1fff75a8;

   uint16_t v_cal130 = *(uint16_t*)0x1fff75ca;

   v_refint         = *(uint16_t*)0x1fff75aa;

   /* We measure at 3.0 Volt versus 3.3V for calibration.

    * Use 3.3/3.0 as correction factor */

   tk = (130.0 - 30.0) / (v_cal130 - v_cal30);

   NutSleep(1);

   while (ADC1->JSQR != jsqr) {

       printf("Failure writing JSQR: %08lx\n", ADC1->JSQR);

       NutSleep(500);

       ADC1->JSQR = jsqr;

   }

   /* Start conversion */

   ADC1->CR |= ADC_CR_ADSTART;

}

THREAD(Adc2Init, arg)

{

   HANDLE *data_ready = (HANDLE *) arg;

   HANDLE                adc_ready = 0;

   /* Wake up from deep power down.*/

   ADC2->CR = 0;

   /* Remove Injected Queue disabled.*/

   ADC2->CFGR = 0;

   ADC2->CR |= ADC_CR_ADVREGEN;

   NutMicroDelay(20);

   /* Calibrate in single ended mode */

   ADC2->CR |= ADC_CR_ADCAL;

   while(ADC2->CR & ADC_CR_ADCAL)

       __NOP();

   /* Calibrate in differential Mode */

   ADC2->CR |= ADC_CR_ADCALDIF;

   ADC2->CR |= ADC_CR_ADCAL;

   while(ADC2->CR & ADC_CR_ADCAL)

       __NOP();

   ADC2->CR = ADC_CR_ADVREGEN;

   if (Stm32DmaGet(&dma_info_adc2, dma_adc2_list)) {

       printf("Stm32DmaGet failed\n");

   }

   if (Stm32DmaRegisterHandler(&dma_info_adc2, adc2_dma_irq, &adc_ready)) {

       printf("Stm32DmaRegisterHandler failed\n");

   }

   Stm32DmaSetup(&dma_info_adc2, adc2_sample_data, (void*)&ADC2->DR,

             ADC_ION_SAMPLES * 2,

             0 * DMA_CCR_PL_0   | /* Low Priority */

             1 * DMA_CCR_PSIZE_0 | /* 16 bit peripheral size */

             1 * DMA_CCR_MSIZE_0 | /* 16 bit memory size */

             DMA_CCR_MINC       |

             DMA_CCR_CIRC       | /* Circular mode */

             DMA_CCR_HTIE       | /* Half transfer interrupt */

             DMA_CCR_TCIE );      /* Full transfer interrupt*/

   /* Get DMA running */

   Stm32DmaEnable(&dma_info_adc2);

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   ADC2->CFGR =

       ADC_CFGR_CONT  | /* Continous mode */

       ADC_CFGR_DMACFG | /* Circular DMA*/

       ADC_CFGR_DMAEN;  /* Enable DMA*/

   ADC2->CR |= ADC_CR_ADEN;

   while((ADC2->ISR & ADC_ISR_ADRDY) == 0) {

       __NOP();

   }

   /* Select single conversion mode (CONT = 0)*/

   /* Select 12 bit resolution by keeping CFGR_RES = 0. */

   /* Select channel 11 (PA6/7) as single conversion*/

   /* Channel 11 differential und als einziger Kanal*/

   ADC2->DIFSEL = ADC_DIFSEL_DIFSEL_11;

   ADC2->SQR1 |= (11 << ADC_SQR1_SQ1_Pos);

   /* Select longest sampling time for all channels. */

   ADC2->SMPR1 = 0x3ffffff8;

   ADC2->SMPR2 = 0x07ffffff;

   NutSleep(1);

   /* Enable 16 times oversampling, no shift */

   ADC2->CFGR2 = ADC_CFGR2_ROVSE | (3 * ADC_CFGR2_OVSR_0) |

       (0 * ADC_CFGR2_OVSS_0);

   ADC2->CR |= ADC_CR_ADSTART;

   while(1) {

       NutEventWait(&adc_ready, NUT_WAIT_INFINITE);

       uint32_t uint_sum = 0;

       uint32_t *buf = adc2_sample_data[buffer_index];

       for (int i = 0; i < ADC_ION_SAMPLES / 2; i++) {

           uint32_t data = *buf++;

           uint_sum += (data >> 16) + (data & 0xffff);

       }

       /* Remove Offset to get signed number */

       ion_sum = uint_sum - 0x8000 * ADC_ION_SAMPLES;

       NutEventPost(data_ready);

   }

}

FArza.1
Associate II

Okay, i'll try to comprehend that, if i got stuck i will post here back hehehe, thank you beforehand.

FArza.1
Associate II

Nice, it took me some time to figure out the HAL equivalent, but got it to work!, thank you very much!.