2014-08-29 10:35 AM
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-stm32f407ie2014-08-29 11:00 AM
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-DMA2014-08-29 11:03 AM
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?Thanks2014-08-29 11:23 AM
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¤tviews=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
/**************************************************************************************/
2014-08-29 10:26 PM
Thank you for the response and the sample code. I was not properly configuring the BDTR TIM register.
2024-03-22 12:11 AM
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);
}