Skip to main content
Visitor II
August 29, 2014
Question

Triggering ADC Sampling by Timer Events

  • August 29, 2014
  • 5 replies
  • 2887 views
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
    This topic has been closed for replies.

    5 replies

    Graduate II
    August 29, 2014
    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
    Visitor II
    August 29, 2014
    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

    Graduate II
    August 29, 2014
    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
    /**************************************************************************************/

    Visitor II
    August 30, 2014
    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.

    Explorer II
    March 22, 2024

    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
    
    
    }

     

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