cancel
Showing results for 
Search instead for 
Did you mean: 

Triggering ADC Sampling by Timer Events

mmilutinovic
Associate II
Posted on August 29, 2014 at 19:35

I am trying to setup ADC sampling that is triggered by a Timer. Essentially I want to setup a 1 kHz timer that will trigger an ADC sample every 1 ms, and use DMA to transfer the sample data.

I have all the components working separately but I cannot get the ADC to sample on a timer event. In the ADC documentation it lists that events from TIM1 �

Timer 1 CC1 event

� can be used as triggers to initiate a sampling. I have setup the �

EXTEN

� and �

EXTSEL

� fields to use this event, but it does not get triggered by the timer.

I have verified that the timer is generating interrupts at 1Khz by setting up an interrupt handler that will manually initiate an ADC sampling via the �

SWSTART

� bit in the ADC->CR2 register. Using this method I can accomplish the task I want, but it adds delays which are causing me to miss samples.

The documentation is not very clear as to how the �

Timer 1 CC1 event

� event gets generated by TIM1. I�m using the �RM0090 Reference manual� for ST STM32F407IE. I�ve spent some time googling and I�ve found people having issues with this as well, with no solutions.

Are there any other documents that detail how this can be setup/used?

#st-stm32f407ie
5 REPLIES 5
Posted on August 29, 2014 at 20:00

I’ve spent some time googling and I’ve found people having issues with this as well, with no solutions.

I know I've posted dozens of examples, perhaps not using TIM1, but with TIM-ADC-DMA
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mmilutinovic
Associate II
Posted on August 29, 2014 at 20:03

Are the posts on this forum? Do you remember for what chip this was for specifically?

If possible could you provide me with a link to to your posts?

Thanks

Posted on August 29, 2014 at 20:23

Yes on this forum, lots of examples, lots of chips, perhaps best searched with Google, because the search here ***** so bad, as does cataloguing others posts.

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20trigger%20on%20timer%20update&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=453

Here's your specific case

// STM32 ADC Sample @ 1 KHz (PC.1) STM32F4 Discovery - sourcer32@gmail.com
// Assumptions per system_stm32f4xx.c CPU @ 168 MHz, APB2 @ 84 MHz (/2), APB1 @ 42 MHz (/4)
#include ''stm32f4_discovery.h''
/**************************************************************************************/
void RCC_Configuration(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* ADC Channel 11 -> PC1
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/**************************************************************************************/
void ADC_Configuration(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* ADC Common Init */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel 11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_15Cycles); // PC1
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
}
/**************************************************************************************/
#define BUFFERSIZE 200 // 1KHz x2 HT/TC at 10Hz
__IO uint16_t ADCConvertedValues[BUFFERSIZE];
static void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCConvertedValues[0];
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = BUFFERSIZE; // Count of 16-bit words
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* Enable DMA Stream Half / Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
/**************************************************************************************/
void TIM1_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // 1 MHz, from 168 MHz TIM1CLK (ie APB2 = HCLK/2, TIM1CLK = HCLK/1)
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 1 MHz -> 1 KHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* TIM PWM1 Mode configuration */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = 10; // Some arbitrary value
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
/* Output Compare PWM1 Mode configuration: Channel1 */
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void DMA2_Stream0_IRQHandler(void) // Called at 10 Hz for 1 KHz sample rate, LED Toggles at 5 Hz
{
/* Test on DMA Stream Half Transfer interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))
{
/* Clear DMA Stream Half Transfer interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
/* Turn LED3 off: Half Transfer */
STM_EVAL_LEDOff(LED3);
// Add code here to process first half of buffer (ping)
}
/* Test on DMA Stream Transfer Complete interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
/* Turn LED3 on: End of Transfer */
STM_EVAL_LEDOn(LED3);
// Add code here to process second half of buffer (pong)
}
}
/**************************************************************************************/
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
TIM1_Configuration();
DMA_Configuration();
ADC_Configuration();
STM_EVAL_LEDInit(LED3); /* Configure LEDs to monitor program status */
STM_EVAL_LEDOn(LED3); /* Turn LED3 on, 5 Hz means it working */
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
while(1); // Don't want to exit
}
/**************************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf(''Wrong parameters value: file %s on line %d

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**************************************************************************************/

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mmilutinovic
Associate II
Posted on August 30, 2014 at 07:26

Thank you for the response and the sample code. I was not properly configuring the BDTR TIM register.

siddhantabhy
Associate

Does someone has the Embedded C code of the above program. Because I am trying to start continuous ADC conversion on Timer event but I am still unable to achieve the desired result.

Driver file:

#include "stm32f407xx.h" //The code has not been verified yet because cube id stopped working properly typedef struct ADC_struct_multi_conversion_tim{ GPIO_TypeDef* GPIOx; uint32_t pin_number; ADC_TypeDef* ADCx; uint32_t channel_number; // 1,2,3 ... 16 uint32_t channel_sequence; // 1,2,3 ... 16 TIM_TypeDef* TIMx; uint32_t tim_channel_num; }ADC_reg_multi_tim; typedef struct timers_adc_struct{ TIM_TypeDef* TIMx; uint32_t channel_number; uint16_t prescaler; uint32_t ARR; uint32_t output_compare_value; }timers_adc; void timer_enable_for_adc(timers_adc* timy){ if((timy->TIMx)==TIM1){ RCC->APB2ENR |= (1UL<<0); } else if((timy->TIMx)==TIM2){ RCC->APB1ENR |= (1UL<<0); } else if((timy->TIMx)==TIM3){ RCC->APB1ENR |= (1UL<<1); } else if((timy->TIMx)==TIM4){ RCC->APB1ENR |= (1UL<<2); } else if((timy->TIMx)==TIM5){ RCC->APB1ENR |= (1UL<<3); } else if((timy->TIMx)==TIM6){ RCC->APB1ENR |= (1UL<<4); } else if((timy->TIMx)==TIM7){ RCC->APB1ENR |= (1UL<<5); } else if((timy->TIMx)==TIM8){ RCC->APB2ENR |= (1UL<<1); } else if((timy->TIMx)==TIM9){ RCC->APB2ENR |= (1UL<<16); } else if((timy->TIMx)==TIM10){ RCC->APB2ENR |= (1UL<<17); } else if((timy->TIMx)==TIM11){ RCC->APB2ENR |= (1UL<<18); } else if((timy->TIMx)==TIM12){ RCC->APB1ENR |= (1UL<<6); } else if((timy->TIMx)==TIM13){ RCC->APB1ENR |= (1UL<<7); } else if((timy->TIMx)==TIM14){ RCC->APB1ENR |= (1UL<<8); } else{ } } void ADC_GPIO_clock_enable_MC_tim(GPIO_TypeDef* GPIOx){ if(GPIOx == GPIOA){ RCC->AHB1ENR |= (0x1UL << 0); } else if(GPIOx == GPIOB){ RCC->AHB1ENR |= (0x1UL << 1); } else if(GPIOx == GPIOC){ RCC->AHB1ENR |= (0x1UL << 2); } else if(GPIOx == GPIOD){ RCC->AHB1ENR |= (0x1UL << 3); } else if(GPIOx == GPIOE){ RCC->AHB1ENR |= (0x1UL << 4); } else if(GPIOx == GPIOF){ RCC->AHB1ENR |= (0x1UL << 5); } else if(GPIOx == GPIOG){ RCC->AHB1ENR |= (0x1UL << 6); } else if(GPIOx == GPIOH){ RCC->AHB1ENR |= (0x1UL << 7); } else if(GPIOx == GPIOI){ RCC->AHB1ENR |= (0x1UL << 8); } } void ADC_clock_enable_MC_tim(ADC_TypeDef* ADC_number){ if(ADC_number==ADC1){ RCC->APB2ENR |= (1UL<<(8)); } else if(ADC_number==ADC2){ RCC->APB2ENR |= (1UL<<(9)); } else if(ADC_number==ADC3){ RCC->APB2ENR |= (1UL<<(10)); } } void ADC_SQRx_config_MC_tim(ADC_reg_multi_tim* adc){ uint32_t channel_seq = adc->channel_sequence; if(channel_seq<=6){ ((adc->ADCx)->SQR3) |= ((adc->channel_number)<<(((adc->channel_sequence)-1)*5)); } else if(channel_seq<=12){ ((adc->ADCx)->SQR2) |= ((adc->channel_number)<<(((adc->channel_sequence)-7)*5)); } else if(channel_seq<=16){ ((adc->ADCx)->SQR1) |= ((adc->channel_number)<<(((adc->channel_sequence)-13)*5)); ; } } void ADC_TIM_trigger_enable(ADC_reg_multi_tim* adc){ if((adc->TIMx)==TIM1){ switch(adc->tim_channel_num){ case(1): (adc->ADCx)->CR2 |= (0UL<<24); break; case(2): (adc->ADCx)->CR2 |= (1UL<<24); break; case(3): (adc->ADCx)->CR2 |= (2UL<<24); break; default: break; } } else if((adc->TIMx)==TIM2){ switch(adc->tim_channel_num){ case(2): (adc->ADCx)->CR2 |= (3UL<<24); break; case(3): (adc->ADCx)->CR2 |= (4UL<<24); break; case(4): (adc->ADCx)->CR2 |= (5UL<<24); break; default: break; } } else if((adc->TIMx)==TIM3){ switch(adc->tim_channel_num){ case(1): (adc->ADCx)->CR2 |= (7UL<<24); break; default: break; } } else if((adc->TIMx)==TIM4){ switch(adc->tim_channel_num){ case(4): (adc->ADCx)->CR2 |= (9UL<<24); break; default: break; } } else if((adc->TIMx)==TIM5){ switch(adc->tim_channel_num){ case(1): (adc->ADCx)->CR2 |= (10UL<<24); break; case(2): (adc->ADCx)->CR2 |= (11UL<<24); break; case(3): (adc->ADCx)->CR2 |= (12UL<<24); break; default: break; } } else if((adc->TIMx)==TIM8){ switch(adc->tim_channel_num){ case(1): (adc->ADCx)->CR2 |= (13UL<<24); break; default: break; } } (adc->ADCx)->CR2 |= (1UL<<28); } void ADC_config_MC_tim(ADC_reg_multi_tim* adc,timers_adc* timy){ ADC_GPIO_clock_enable_MC_tim(GPIOA); ADC_clock_enable_MC_tim(ADC1); (adc->GPIOx)->MODER |= (1UL<<((adc->pin_number)*2)); // Putting pins into Analog mode (adc->GPIOx)->MODER |= (1UL<<(((adc->pin_number)*2)+1)); // Moreover we are keeping PUPDR to No push pull ADC_SQRx_config_MC_tim(adc); timer_enable_for_adc(timy); (adc->ADCx)->CR1 |= (1UL<<8); //Scan Mode Enable: Forces ADC to check the channels in regular conversion mode (adc->ADCx)->CR1 |= (1UL<<5); // Interrupt Enable //(adc->ADCx)->CR1 |= (1UL<<11); // Discontinuous mode enabled NVIC->ISER[0] |= (1UL<<(18)); // ADC has a global Interrupt in NVIC Register ADC_TIM_trigger_enable(adc); (adc->ADCx)->CR2 |= (1UL<<1); // Continuous conversion turned on (timy->TIMx)->CR1 |= (1UL<<5); // Center aligned mode 1 (timy->TIMx)->CR1 &= ~(1UL<<6); switch(timy->channel_number){ case(1): (timy->TIMx)->CCMR1 |= ((1UL<<4)|(1UL<<5)); // Output compare mode on channel 1 (timy->TIMx)->CCER |= (1UL<<0); // Channel 1 enable (timy->TIMx)->CCR1 = (timy->output_compare_value)-1; break; case(2): (timy->TIMx)->CCMR1 |= ((1UL<<12)|(1UL<<13)); // Output compare mode on channel 2 (timy->TIMx)->CCER |= (1UL<<4); // Channel 2 enable (timy->TIMx)->CCR2 = (timy->output_compare_value)-1; break; case(3): (timy->TIMx)->CCMR2 |= ((1UL<<4)|(1UL<<5)); // Output compare mode on channel 3 (timy->TIMx)->CCER |= (1UL<<8); // Channel 3 enable (timy->TIMx)->CCR3 = (timy->output_compare_value)-1; break; case(4): (timy->TIMx)->CCMR2 |= ((1UL<<12)|(1UL<<13)); // Output compare mode on channel 4 (timy->TIMx)->CCER |= (1UL<<12); // Channel 4 enable (timy->TIMx)->CCR4 = (timy->output_compare_value)-1; break; default: break; } (timy->TIMx)->PSC = (timy->prescaler)-1; (timy->TIMx)->ARR = (timy->ARR)-1; (adc->ADCx)->CR2 |= (1UL<<0); // ADON: ADC Enable (adc->ADCx)->CR2 |= (1UL<<30); // SW Start: Start conversion }
View more

 

main file:

#include <stdio.h> #include <stdlib.h> #include "stm32f407xx.h" #include <stdint.h> #include <stdbool.h> typedef struct ADC_struct_multi_conversion_tim{ GPIO_TypeDef* GPIOx; uint32_t pin_number; ADC_TypeDef* ADCx; uint32_t channel_number; // 1,2,3 ... 16 uint32_t channel_sequence; // 1,2,3 ... 16 TIM_TypeDef* TIMx; uint32_t tim_channel_num; }ADC_reg_multi_tim; typedef struct timers_adc_struct{ TIM_TypeDef* TIMx; uint32_t channel_number; uint16_t prescaler; uint32_t ARR; uint32_t output_compare_value; }timers_adc; void ADC_config_MC_tim(ADC_reg_multi_tim* adc,timers_adc* timy); int main(){ ADC_reg_multi_tim PA1 = {GPIOA,1UL,ADC1,1UL,1UL,TIM2,2UL}; timers_adc tim2_ch2 = {TIM2,2UL,16000,100000,500}; ADC_config_MC_tim(&PA1,&tim2_ch2); while(1); return 0; } void ADC_IRQHandler(){ uint32_t data = ADC1->DR; printf("Data:%ld \n",data); }
View more