cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F429, HAL, TIM+ADC+DMA + (mem2mem)

drmoore
Associate II
Posted on October 01, 2016 at 02:13

*** Apologies for the crosspost, I originally submitted this to the 'tools' forum instead of this one.

Hi,

These forums have been so helpful for me, but I cant seem to find the answer to a problem that has me up for days. Here's what I am trying to achieve using the HAL:

ADC1, ADC2, ADC3 simultaneously sampling through TIM8 update->TRGO. This seems to be working fine. I also need to sample a location in memory and put it into a sample buffer simultaneously with the ADC samples. I know a mem2mem dma is off the table because it will not be gates by TIM8.

I have tried to achieve this through the following:

void Analog_periphStateInitDMA(void)
{
__DMA2_CLK_ENABLE();
/*##-3- Configure the DMA streams ##########################################*/
if (NULL != sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Instance) // Only preform the DeInit if needed
HAL_DMA_DeInit(&sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Instance = DMA2_Stream7;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.Channel = DMA_CHANNEL_7;
IRQn_Type dmaIRQ = DMA2_Stream7_IRQn;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.Direction = DMA_PERIPH_TO_MEMORY;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.PeriphInc = DMA_PINC_DISABLE;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.MemInc = DMA_MINC_ENABLE;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.Mode = DMA_NORMAL;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.Priority = DMA_PRIORITY_HIGH;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.FIFOMode = DMA_FIFOMODE_ENABLE;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.MemBurst = DMA_MBURST_SINGLE;
sAnalog.tim.hdma[TIM_DMA_ID_UPDATE].Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
/* Associate the initialized DMA handle to the the ADC handle */
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_UPDATE], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_CC1], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_CC2], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_CC3], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_CC4], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_COMMUTATION], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
__HAL_LINKDMA(&sAnalog.tim.htim, hdma[TIM_DMA_ID_TRIGGER], sAnalog.tim.hdma[TIM_DMA_ID_UPDATE]);
HAL_NVIC_SetPriority(dmaIRQ, 0, 0); // Configure the NVIC for DMA
HAL_NVIC_EnableIRQ(dmaIRQ);
}
void Analog_configureADC(ADCSelect adcSel, void *pDst, uint32_t numSamps)
{
if (ADC_PERIPH_STATE == adcSel)
{
Analog_periphStateInitDMA();
HAL_DMA_Start_IT(&sAnalog.tim.hdma[TIM_DMA_ID_UPDATE], (uint32_t)&gPeriphState, (uint32_t)pDst, numSamps);
__HAL_TIM_ENABLE_DMA(&sAnalog.tim.htim, TIM_DMA_ID_TRIGGER);
NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
NVIC_ClearPendingIRQ(ADC_IRQn);
return;
}
... // set up the other ADCs
return
}
boolean Analog_setupTimer(uint32_t irqPeriod)
{
//uint32_t timFreq = HAL_RCC_GetPCLK1Freq() * 2;
const uint32_t timFreq = 90000000;
sAnalog.tim.htim.Instance = TIM8;
sAnalog.tim.htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sAnalog.tim.htim.Init.CounterMode = TIM_COUNTERMODE_UP;
sAnalog.tim.htim.Init.Period = irqPeriod * (timFreq / (1000 * 1000));
sAnalog.tim.htim.Init.Prescaler = 0;
sAnalog.tim.htim.Init.RepetitionCounter = 0;
sAnalog.tim.htim.Channel = HAL_TIM_ACTIVE_CHANNEL_1;
HAL_TIM_Base_Init(&sAnalog.tim.htim);
TIM_ClockConfigTypeDef clockDef;
clockDef.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
clockDef.ClockPolarity = TIM_CLOCKPOLARITY_BOTHEDGE;
clockDef.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;
clockDef.ClockFilter = 0;
HAL_TIM_ConfigClockSource(&sAnalog.tim.htim, &clockDef);
TIM_MasterConfigTypeDef sMasterConfig;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&sAnalog.tim.htim, &sMasterConfig);
return TRUE;
}
boolean Analog_startSampleTimer(uint32_t sampRate)
{
Analog_setupTimer(sampRate);
HAL_TIM_Base_Start(&sAnalog.tim.htim);
//HAL_TIM_Base_Start_IT(&sAnalog.tim.htim);
return SUCCESS;
}

The 3 ADCs sample just fine and in lock-step with the timer. The mem2mem (periphState) stream will never move, even when I manually generate EGR->UG triggers -- the ADCs do transfer when I do that.

Am I going about this the right way -- using the DMA triggered off of TIM8 to copy gPeriphState to the sample buffer? Is the DMA going to have a problem since I am telling it that its a peripheral, but its really a memory location -- I thought DMA2 has access to main memory.

Would you recommend I try triggering off of the ADCs instead? I tried this and didn't get anywhere.

Can you pick out a bug that is causing this not to work? This is my first project with the HAL and it has been a huge learning curve. I have run into behavior that I didn't expect before.

Thank you so much for your help in advance.

#stm32f429-hal-tim-adc-dma-mem
3 REPLIES 3
drmoore
Associate II
Posted on October 01, 2016 at 02:48

I made some modifications to the code and now I am much closer.

Now: whenever I manually trigger EGR->TG (generate trigger), the mem2mem works properly. Whenever I trigger the EGR->UG (generate update), all of the ADCs sample and transfer data properly. I don't quite understand why this is happening,I have configured the ADCs to trigger off of the TRGO:

void Analog_initADC(ADCSelect adcSel)
{
ADCPort adcNum = Analog_getPortNumber(adcSel);
ADCPortCfg *pADC = &sAnalog.adc[adcNum];
if (HAL_ADC_STATE_RESET != pADC->hadc.State)
HAL_ADC_DeInit(&pADC->hadc);
if (HAL_DMA_STATE_RESET != pADC->hdma.State)
HAL_DMA_DeInit(&pADC->hdma);
switch (adcNum)
{
case ADC_PORT1: pADC->hadc.Instance = ADC1; break;
case ADC_PORT2: pADC->hadc.Instance = ADC2; break;
case ADC_PORT3: pADC->hadc.Instance = ADC3; break;
default: return;
}
// 180MHz div6 = 30Mhz clock
pADC->hadc.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV6;
pADC->hadc.Init.Resolution = ADC_RESOLUTION_12B;
pADC->hadc.Init.ScanConvMode = DISABLE;
pADC->hadc.Init.ContinuousConvMode = DISABLE;
pADC->hadc.Init.DiscontinuousConvMode = DISABLE;
pADC->hadc.Init.NbrOfDiscConversion = 0;
pADC->hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
pADC->hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
pADC->hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
pADC->hadc.Init.NbrOfConversion = 1;
pADC->hadc.Init.DMAContinuousRequests = DISABLE;
pADC->hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
HAL_ADC_Init(&pADC->hadc);
ADC_ChannelConfTypeDef chanConfig;
chanConfig.Channel = Analog_getChannelNumber(adcSel);
chanConfig.Rank = 1;
chanConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
chanConfig.Offset = 0;
HAL_ADC_ConfigChannel(&pADC->hadc, &chanConfig);
Analog_dmaInit(adcNum);
}

But they seem to be only responding to update events? Does it have something to do with the way I have configured the trigger select when I am configuring the timer:

boolean Analog_setupTimer(uint32_t irqPeriod)
{
//uint32_t timFreq = HAL_RCC_GetPCLK1Freq() * 2;
const uint32_t timFreq = 90000000;
sAnalog.tim.htim.Instance = TIM8;
sAnalog.tim.htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sAnalog.tim.htim.Init.CounterMode = TIM_COUNTERMODE_UP;
sAnalog.tim.htim.Init.Period = irqPeriod * (timFreq / (1000 * 1000));
sAnalog.tim.htim.Init.Prescaler = 0;
sAnalog.tim.htim.Init.RepetitionCounter = 0;
sAnalog.tim.htim.Channel = HAL_TIM_ACTIVE_CHANNEL_1;
HAL_TIM_Base_Init(&sAnalog.tim.htim);
TIM_ClockConfigTypeDef clockDef;
clockDef.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
clockDef.ClockPolarity = TIM_CLOCKPOLARITY_BOTHEDGE;
clockDef.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;
clockDef.ClockFilter = 0;
HAL_TIM_ConfigClockSource(&sAnalog.tim.htim, &clockDef);
TIM_MasterConfigTypeDef sMasterConfig;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
//sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
//sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&sAnalog.tim.htim, &sMasterConfig);
__HAL_TIM_ENABLE_DMA(&sAnalog.tim.htim, TIM_DMA_UPDATE);
__HAL_TIM_ENABLE_DMA(&sAnalog.tim.htim, TIM_DMA_TRIGGER);
return TRUE;
}

I have the master output trigger set to update ... and I read that as 'upon update, generate a trigger' is that correct? Thanks!
drmoore
Associate II
Posted on October 01, 2016 at 11:22

I solved it.

I was misreading the reference manual documentation. DMA2 Stream7, Channel7 is the TIM_TRIG channel, but this is

NOT

the same as the TRGO mentioned in the HAL for triggering ADCs. I think the TIM_TRIG pertains to external trigger sources. The TRGO is the output trigger of the TIM module that is the result of the multiplexer -- for which update is one of the possible inputs. I moved the memory stream/channel over to TIM8_UP and everything works swimmingly. Hope this helps someone in the future! Regards, Dan

void Analog_dmaInit(ADCPort adcNum)
{
IRQn_Type dmaIRQ;
ADC_HandleTypeDef *phADC;
TIM_HandleTypeDef *phTIM;
DMA_HandleTypeDef *phDMA;
if (ADC_PORT_PERIPH_STATE == adcNum)
{
phTIM = &sAnalog.tim.htim;
phDMA = &sAnalog.tim.hdma[TIM_DMA_ID_UPDATE];
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_UPDATE], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_CC1], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_CC2], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_CC3], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_CC4], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_COMMUTATION], *phDMA);
__HAL_LINKDMA(phTIM, hdma[TIM_DMA_ID_TRIGGER], *phDMA);
phDMA->XferCpltCallback = Analog_PeriphStateXferCpltCallback;
}
else
{
phADC = &sAnalog.adc[adcNum].hadc;
phDMA = &sAnalog.adc[adcNum].hdma;
__HAL_LINKDMA(phADC, DMA_Handle, *phDMA);
}
__DMA2_CLK_ENABLE(); // Configure the DMA streams
if (NULL != phDMA->Instance) // Only preform the DeInit if it was previously init
HAL_DMA_DeInit(phDMA);
switch (adcNum) // Configure the DMA streams 
{
case ADC_PORT1:
phDMA->Instance = DMA2_Stream4;
phDMA->Init.Channel = DMA_CHANNEL_0;
dmaIRQ = DMA2_Stream4_IRQn;
break;
case ADC_PORT2:
phDMA->Instance = DMA2_Stream2;
phDMA->Init.Channel = DMA_CHANNEL_1;
dmaIRQ = DMA2_Stream2_IRQn;
break;
case ADC_PORT3:
phDMA->Instance = DMA2_Stream0;
phDMA->Init.Channel = DMA_CHANNEL_2;
dmaIRQ = DMA2_Stream0_IRQn;
break;
case ADC_PORT_PERIPH_STATE:
phDMA->Instance = DMA2_Stream1;
phDMA->Init.Channel = DMA_CHANNEL_7;
dmaIRQ = DMA2_Stream1_IRQn;
break;
default: return;
}
phDMA->Init.Direction = DMA_PERIPH_TO_MEMORY;
phDMA->Init.PeriphInc = DMA_PINC_DISABLE;
phDMA->Init.MemInc = DMA_MINC_ENABLE;
phDMA->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
phDMA->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
phDMA->Init.Mode = DMA_NORMAL;
phDMA->Init.Priority = DMA_PRIORITY_HIGH;
phDMA->Init.FIFOMode = DMA_FIFOMODE_ENABLE;
phDMA->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
phDMA->Init.MemBurst = DMA_MBURST_SINGLE;
phDMA->Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(phDMA);
HAL_NVIC_SetPriority(dmaIRQ, 0, 0); // Configure the NVIC for DMA
HAL_NVIC_EnableIRQ(dmaIRQ);
}
void Analog_initADC(ADCSelect adcSel)
{
ADCPort adcNum = Analog_getPortNumber(adcSel);
ADCPortCfg *pADC = &sAnalog.adc[adcNum];
if (HAL_ADC_STATE_RESET != pADC->hadc.State)
HAL_ADC_DeInit(&pADC->hadc);
if (HAL_DMA_STATE_RESET != pADC->hdma.State)
HAL_DMA_DeInit(&pADC->hdma);
switch (adcNum)
{
case ADC_PORT1: pADC->hadc.Instance = ADC1; break;
case ADC_PORT2: pADC->hadc.Instance = ADC2; break;
case ADC_PORT3: pADC->hadc.Instance = ADC3; break;
default: return;
}
// 180MHz div6 = 30Mhz clock
pADC->hadc.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV6;
pADC->hadc.Init.Resolution = ADC_RESOLUTION_12B;
pADC->hadc.Init.ScanConvMode = DISABLE;
pADC->hadc.Init.ContinuousConvMode = DISABLE;
pADC->hadc.Init.DiscontinuousConvMode = DISABLE;
pADC->hadc.Init.NbrOfDiscConversion = 0;
pADC->hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
pADC->hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
pADC->hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
pADC->hadc.Init.NbrOfConversion = 1;
pADC->hadc.Init.DMAContinuousRequests = DISABLE;
pADC->hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
HAL_ADC_Init(&pADC->hadc);
ADC_ChannelConfTypeDef chanConfig;
chanConfig.Channel = Analog_getChannelNumber(adcSel);
chanConfig.Rank = 1;
chanConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
chanConfig.Offset = 0;
HAL_ADC_ConfigChannel(&pADC->hadc, &chanConfig);
Analog_dmaInit(adcNum);
}
boolean Analog_setupTimer(uint32_t irqPeriod)
{
//uint32_t timFreq = HAL_RCC_GetPCLK1Freq() * 2;
const uint32_t timFreq = 90000000;
sAnalog.tim.htim.Instance = TIM8;
sAnalog.tim.htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sAnalog.tim.htim.Init.CounterMode = TIM_COUNTERMODE_UP;
sAnalog.tim.htim.Init.Period = irqPeriod * (timFreq / (1000 * 1000));
sAnalog.tim.htim.Init.Prescaler = 0;
sAnalog.tim.htim.Init.RepetitionCounter = 0;
sAnalog.tim.htim.Channel = HAL_TIM_ACTIVE_CHANNEL_1;
HAL_TIM_Base_Init(&sAnalog.tim.htim);
TIM_ClockConfigTypeDef clockDef;
clockDef.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
clockDef.ClockPolarity = TIM_CLOCKPOLARITY_BOTHEDGE;
clockDef.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;
clockDef.ClockFilter = 0;
HAL_TIM_ConfigClockSource(&sAnalog.tim.htim, &clockDef);
TIM_MasterConfigTypeDef sMasterConfig;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&sAnalog.tim.htim, &sMasterConfig);
__HAL_TIM_ENABLE_DMA(&sAnalog.tim.htim, TIM_DMA_UPDATE);
return TRUE;
}
void Analog_configureADC(ADCSelect adcSel, void *pDst, uint32_t numSamps)
{
if (ADC_PERIPH_STATE == adcSel)
{
Analog_dmaInit(ADC_PORT_PERIPH_STATE);
HAL_DMA_Start_IT(&sAnalog.tim.hdma[TIM_DMA_ID_UPDATE], (uint32_t)&gPeriphState, (uint32_t)pDst, numSamps);
}
else
{
Analog_initADC(adcSel);
ADCPortCfg *pCfg = Analog_getADCPortConfig(adcSel);
pCfg->cfg.adcConfig.numSamps = numSamps;
pCfg->cfg.appSampleBuffer = pDst;
pCfg->cfg.adcConfig.chan[0].chanNum = Analog_getChannelNumber(adcSel);
numSamps = MIN(0xFFFF, numSamps);
if(HAL_ADC_Start_DMA(&pCfg->hadc, (uint32 *)pDst, numSamps) != HAL_OK)
Error_Handler();
}
}
boolean Analog_startSampleTimer(uint32_t sampRate)
{
Analog_setupTimer(sampRate);
BSP_LED_On(LED3);
HAL_TIM_Base_Start(&sAnalog.tim.htim);
return SUCCESS;
}

Walid FTITI_O
Senior II
Posted on October 05, 2016 at 13:58

Hi nmoore.daniel.001, 

Thank you for the knowledge share. This may help other users.

-Hannibal-