STM32F4: ¿Cómo obtener datos de múltiples canales muestreados desde un solo ADC?

0

Así que he buscado por todas partes una solución a este problema, pero puedo encontrar cualquier cosa. Tengo tres canales conectados a ADC1 de mi STM32F446ZE-nucleo y quiero muestrear los canales a una frecuencia de 200 Hz cada uno. Configuré TIM2 y lo vinculé como la interrupción para la conversión de ADC, y esto funciona bien para 1 canal. Pero estoy luchando para que funcione para varios canales, ya que no sé cómo obtener los datos de cada canal específico dentro de HAL_ADC_ConvCpltCallback .

Mi configuración se ve así:

/* 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__);
  }

}

Comienzo mi ADC y el temporizador después de recibir datos en USART3 así:

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

Aquí es donde estoy luchando para obtener los datos correctos:

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);
}

Pensé que hadc->NbrOfCurrentConversionRank debería tener información sobre qué canal se está muestreando, pero es constantemente 0 (pensé que iría de 1 a 2 a 3). ¿Cómo hago para obtener los datos del canal individual en cada interrupción de conversión completa? Cualquier ayuda sería apreciada :)

    
pregunta Loupi

1 respuesta

0

Esto debería ser un comentario para preguntarle si desea mi solución, que incluye 3 señales ADC x 3 Canales = 9, utilizando periféricos DMA y no bibliotecas HAL. Como mi representante es < 50, no puedo hacer ese comentario, así que proporcionaré el código para lo anterior: P

Utiliza una técnica Maestro-Esclavo, con ADC1 configurado como maestro y ADC2 y 3 como esclavos. Las interrupciones se producen según la frecuencia de TIM2, que se puede modificar.

Espero que ayude :)

Tablero μC: STM32F429ZI-Discovery

IDE: μVision V5.13.0.0

HabilitarTIM2

#defineADC_PER4499RCC_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=>10kHzTIM_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);

HabilitarrelojADC

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

ADC configurado en Modo triple, Habilitar DMA, Sin precalificación

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 conversiones para cada ADC, por lo tanto 9 en total. La resolución más alta de 12 bits, la alineación correcta de los datos y el desencadenador de eventos por interrupciones TIM2.

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);

Establezca cada canal en el pin derecho, con 15 ciclos para muestrear la señal

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);

Habilitar reloj DMA.

Configure el transporte de datos DMA, la dirección base, el tamaño de la matriz, la transferencia de datos de media palabra y el modo circular.

Habilitar Stream4 de 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)

Configure y habilite el evento de interrupción de 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); 

Configura ADC1 como Maestro y ADC2 y ADC3 como Esclavos, habilita DMA y todos los ADC.

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

ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_Cmd(ADC3, ENABLE);
    
respondido por el thece

Lea otras preguntas en las etiquetas