0
\$\begingroup\$

So I have searched everywhere for a solution to this problem but I can find anything. I have three channels connected to ADC1 of my STM32F446ZE-nucleo and I want to sample the channels at a rate of 200 Hz each. I set up TIM2 and linked it as the interrupt for the ADC conversion, and this works fine for 1 channel. But I am struggling to get it to work for multiple channels since I don't know how to get the data from each specific channel within the HAL_ADC_ConvCpltCallback.

My setup looks like this:

/* ADC1 init function */
static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 3;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

/* TIM2 init function */
static void MX_TIM2_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 41999;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

I start my ADC and timer after receiving data on USART3 like this:

// start timer and ADC interrupt
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_Base_Start(&htim2);

This is where I am struggling to get the right data:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{

    // get values
    WE_ADC_VAL = HAL_ADC_GetValue(hadc);

    uint32_t nrConv = hadc->NbrOfCurrentConversionRank;

    // populate buffer
    transmitBuf[0] = WE_ADC_VAL >> 8; // upper 8 bits
    transmitBuf[1] = WE_ADC_VAL & 0x00FF; // lower 8 bits

    // send by interrupt mode
    HAL_UART_Transmit_IT(&huart3, transmitBuf, 2);
}

I thought that hadc->NbrOfCurrentConversionRank should have info on which channel is being sampled, but it is constantly 0 (I thought it would go from 1 to 2 to 3). How would I go about to get the individual channel's data at each conversion complete interrupt? Any help would be appreciated :)

\$\endgroup\$
  • \$\begingroup\$ Ask a specific question, you'll get better answers. \$\endgroup\$ – laptop2d Jul 29 '18 at 0:22
0
\$\begingroup\$

This should be a comment to ask you if you want my solution, which includes 3 ADCs x 3 Channels = 9 signals, by utilizing DMA peripheral and no HAL libraries. Since my rep is < 50, I cannot make that comment, so I will provide the code for the above :P

It utilizes a Master-Slave technique, with ADC1 set as the master and ADC2 and 3 as slaves. Interrupts occur based on TIM2 frequency, which can be modified.

Hope it helps :)


μC Board: STM32F429ZI-Discovery

IDE: μVision V5.13.0.0

enter image description here

TIM2 Enable

#define ADC_PER 4499

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_BaseStruct_ST.TIM_Prescaler = 0;
TIM_BaseStruct_ST.TIM_CounterMode = TIM_CounterMode_Up;
// 899 => 100kHz, 2249 => 40kHz, 4499 => 20kHz, 8999 => 10kHz
TIM_BaseStruct_ST.TIM_Period = ADC_PER; 
TIM_BaseStruct_ST.TIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseInit(TIM2, &TIM_BaseStruct_ST);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

ADC Clock Enable

#include "stm32f4xx_adc.h"
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC2, ENABLE);
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC3, ENABLE);

ADC set to Triple Mode, DMA Enable, No Prescaler

ADC_CommonInitTypeDef ADC_CommonInitStructure;

ADC_CommonInitStructure.ADC_Mode          = ADC_TripleMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler     = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
ADC_CommonInit (&ADC_CommonInitStructure);

3 conversions for each ADC, hence 9 in total. Highest resolution of 12 bits, right data alignment and event trigger by TIM2 interrupts.

ADC_InitTypeDef ADC_InitStructure;

ADC_InitStructure.ADC_ScanConvMode       = ENABLE;
ADC_InitStructure.ADC_NbrOfConversion    = 3;
ADC_InitStructure.ADC_Resolution         = ADC_Resolution_12b;
ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;

TIM_SelectOutputTrigger (TIM2, TIM_TRGOSource_Update);
ADC_InitStructure.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;

ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_Init(ADC3, &ADC_InitStructure);

Set each channel to the right pin, with 15 cycles for sampling the signal

ADC_RegularChannelConfig (ADC1, ADC_Channel_13, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC1, ADC_Channel_8,  2, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC1, ADC_Channel_6,  3, ADC_SampleTime_15Cycles);

ADC_RegularChannelConfig (ADC2, ADC_Channel_0,  1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC2, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC2, ADC_Channel_14, 3, ADC_SampleTime_15Cycles);

ADC_RegularChannelConfig (ADC3, ADC_Channel_15, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC3, ADC_Channel_9,  2, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig (ADC3, ADC_Channel_3,  3, ADC_SampleTime_15Cycles);

DMA Clock Enable.

Setup DMA data transport, base address, matrix size, half word data transfer and circular mode.

Enable Stream4 of DMA2.

#include "stm32f4xx_dma.h"
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

DMA_InitTypeDef DMA_InitStructure;

DMA_InitStructure.DMA_DIR     = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR;
DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)(&adc[0]);
DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_BufferSize = 9;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_Init(DMA2_Stream4, &DMA_InitStructure)

Setup and enable interrupt event of DMA

NVIC_InitTypeDef ADCNVICConfig;
ADCNVICConfig.NVIC_IRQChannel = DMA2_Stream4_IRQn;
ADCNVICConfig.NVIC_IRQChannelPreemptionPriority = 0;
ADCNVICConfig.NVIC_IRQChannelSubPriority = 1;
ADCNVICConfig.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&ADCNVICConfig);

ADC_DMACmd(ADC1, ENABLE);
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
DMA_ITConfig(DMA2_Stream4, DMA_IT_TC, ENABLE);

DMA_Cmd(DMA2_Stream4, ENABLE); 

Setup ADC1 as Master and ADC2 and ADC3 as Slaves, enable DMA and all ADCs.

ADC_DMACmd(ADC1, ENABLE);
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_Cmd(ADC3, ENABLE);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.