STM32F10x Timer+ADC+DMA Kullanımı hakkında

Başlatan Mucit23, 30 Haziran 2015, 13:02:12

Mucit23

Selamlar

Arkadaşlar STM32'de Timer kullanarak ADC den 2'ms aralıklarla örnek alıp DMA ile İstediğim bir değişkene atmam lazım. En sonda DMA Transfer complete kesmesini aktif edip veriyi kesmeden almak istiyorum.

Bir takım işleri hallettim. Örneğin ADC yi Software start konumuna alarak sürekli örnek almasını sağlayıp DMA ile istediğim değişkene ADC değerinin taşınmasını sağladım. Hatta DMA TC kesmeside istediğim gibi çalışıyor. Fakat ADC tetikleme işini timer ile yapamadım. Bu konuda internette STM32F4x ile yapılmış örnekler var. Onlarak bakarak STM32F10X de yapmaya çalıştım ama çalıştıramadım bir türlü.

ADC, DMA ve Timer Kurulum ayarlarım aşağıdaki gibi.

#include "stm32f10x.h"
#include "Adc.h"

unsigned int ADC_ConvertedValue[316];

/*ADC baslanagic ayarlari yuklenme islemini yapar */
void ADC1_Configuration(){
	
		ADC_InitTypeDef ADC_InitStructure;
		GPIO_InitTypeDef GPIO_InitStructure;

		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

		/* ADC pin analog input yapiliyor */
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
		GPIO_Init(GPIOC, &GPIO_InitStructure);

		ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
		ADC_InitStructure.ADC_ScanConvMode = ENABLE;
		ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
		ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_CC1;
		ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
		ADC_InitStructure.ADC_NbrOfChannel = 1;
		ADC_Init(ADC1, &ADC_InitStructure);

		/* ADC1 Kanal ve clk secimi yapiliyor ( kanal_15 )  */ 
		ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_55Cycles5);

		ADC_Cmd(ADC1, ENABLE);
		
		ADC_DMACmd(ADC1,ENABLE);
		
		ADC_ExternalTrigConvCmd(ADC1,ENABLE);

		/* ADC1 Reset Kalibrasyonu yapilacak */
		ADC_ResetCalibration(ADC1);
		/* Kalibrasyon bekleniyor */
		while(ADC_GetResetCalibrationStatus(ADC1));

		/* ADC1 calibrasyonu yapilacak */
		ADC_StartCalibration(ADC1);
		/* Kalibrasyon bekleniyor */
		while(ADC_GetCalibrationStatus(ADC1));
		
		/*  ADC yi Baslatiyoruz  */
		//ADC_SoftwareStartConvCmd(ADC1,ENABLE); 
}

void DMA1_Configuration(void)
{
		DMA_InitTypeDef DMA_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
		/*DMA Clock Aktif Ediliyor*/
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

		/*  DMA Ayarlar1 Yapiliyor */
		DMA_DeInit(DMA1_Channel1);
		DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
		DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue[315];
		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
		DMA_InitStructure.DMA_BufferSize = 1;
		DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
		DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
		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_M2M = DMA_M2M_Disable;
		DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
		 /* Enable the TIM1 Interrupt */
		NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		
		/* DMA Transfer Complete interrupt Enable*/
		DMA_ITConfig(DMA1_Channel1,DMA1_IT_TC1,ENABLE);	

		/* Enable DMA1 channel1 */
		DMA_Cmd(DMA1_Channel1, ENABLE);
}

void TIM3_Configuration(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;			
    /*Enable TIM2 Clock*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);		
    TIM_DeInit(TIM3);
    
    TIM_TimeBaseStructure.TIM_Period = 2500;	
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  
	  TIM_ARRPreloadConfig(TIM3, ENABLE);
	
    TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update); 
	
    TIM_Cmd(TIM3, ENABLE);
}

void ADC_init(void)
{
  TIM3_Configuration();
  DMA1_Configuration();
	ADC1_Configuration();
}


Mutlaka atladığım bir yer olduğunu düşünüyorum. Aksi durumda çalışmaması için bir sebeb yok.

Yardımcı olursanız sevinirim.

justice_for_all

#1
Alıntı Yap#define ADC_ExternalTrigConv_T3_CC1                ((uint32_t)0x00000000) /*!< For ADC3 only */

ADC_InitStructure.ADC_ExternalTrigConv =ADC_ExternalTrigConv_T3_TRGO;


bunu yaparsanız olabilir.
Deneyip de başaramayanları değil, yalnızca denemeye bile kalkışmayanları yargıla.   Gökhan Arslanbay

Mucit23

#2
Alıntı yapılan: justice_for_all - 30 Haziran 2015, 13:23:19


Cevap için teşekkürler.

Ben ADC1 kullanıyorum. ADC Tetik girişi için CC1 kullanamam

ADC tetik girişini aşağıdaki gibi yaptım
      ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;

TIM3 ayarlarınıda aşağıdaki gibi yaptım. OC'yi aktif edip Trigger çıkışını OC1 olarak seçtim
void TIM3_Configuration(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	  TIM_OCInitTypeDef	TIM_OCInitStructure;
	
    /*Enable TIM2 Clock*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);		
    TIM_DeInit(TIM3);
    
    TIM_TimeBaseStructure.TIM_Period = 50000;	
    TIM_TimeBaseStructure.TIM_Prescaler = 35;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
	
	  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
	
    TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_OC1); 
	
    TIM_ARRPreloadConfig(TIM3, ENABLE);	
	
    TIM_Cmd(TIM3, ENABLE);
}


Nitekim DMA kesmeleri oluşmaya başladı. Ama istediğim frekansı tutturamadım.

DMA interrupt rutinim ise aşağıdaki gibi
void DMA1_Channel1_IRQHandler(void)
{
	GPIOD->ODR^=GPIO_Pin_8;
	DMA_ClearITPendingBit(DMA1_IT_TC1);
}


GPIOD'nin 8. pinine led bağlı. Bende toggle yapıyorum ki frekans ölçeyim. Yalnız yanımda şuan skop yok. Timerin düzgün çalıştığını anlamam için 50ms aralıklarla örnek alınmasını sağlamam lazım. Maksat ledin yanıp söndüğünü gözle görebileyim. Timer için aşağıdaki hesaplamaları yaptım.

İşlemci 72MHZ de çalışıyor. TIM3 APB1 den beslendiği için 72/2=36Mhz giriş frekansı var.

Prescaller değerini 35(36-1) yaparsam Timer 1MHz ile çalışmış olur.

Timer Update frekansı 1000ms/50ms den 20Hz olması gerekiyor. Ohalde 1000000/20=50000 Yani Preload değerim 50000 olması gerekiyor.

Yanlışım yoksa hesaplamalar böyle yapılıyordu. Sonuç olarak ADC çok hızlı bir şekilde çalışıyor. 50ms aralıklarla ölçüm alınmıyor. Led sürekli yanıyor gibi görünüyor.

Hocam Timer hesabımdamı yanlışlık var? Yoksa Hala ADC - Timer trigger seçimlerimi yanlış?

justice_for_all

#3
periyot 60000 prescaler da 30 yaparsanız 20 hz elde edersiniz.

mesaj birleştirme:: 30 Haziran 2015, 14:08:57

        ADC_InitStructure.ADC_ScanConvMode = DISABLE;
        ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;


Yapıp denermisiniz hocam bide.
Deneyip de başaramayanları değil, yalnızca denemeye bile kalkışmayanları yargıla.   Gökhan Arslanbay

Mucit23

Evet bunu atlamışım işte. Continuous mode etkin kalmış. Teşekkürler.  Şimdi istediğim gibi çalışmaya başladı.

Frekans için hem benim verdiğim değerleri  hemde sizin verdiğiniz değerleri denedim. Fark yok gibi.

justice_for_all

zaten gözle farkedemezsin o farkı osilaskop yada lojikle ölçünce anlaşılır.
Deneyip de başaramayanları değil, yalnızca denemeye bile kalkışmayanları yargıla.   Gökhan Arslanbay

Mucit23

Benim hesap yanlışmıydı? Siz nasıl hesapladınız? Yada nasıl hesaplıyorsunuz?

justice_for_all

#7
sysclkfreq / ((prescaler + 1 ) * (periyot + 1)) burdan hz olarak bulursun.

aslında bemim verdiğin değerler yalnış prescaler 29 periyot ise 59999 olması gerkiyor.

seninkide dogru aslında sadece degerler değişiyor.
Deneyip de başaramayanları değil, yalnızca denemeye bile kalkışmayanları yargıla.   Gökhan Arslanbay

Mucit23