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


}

 

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

}