Stm32f4 ADC problemi

Başlatan Enginar, 08 Mart 2013, 14:43:46

Enginar

Basit bir ADC uygulaması yapmaya çalışıyorum kodlarım şu şekilde

#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_tim.h"
#include "misc.h"
#include "stm32f4xx_flash.h"
#include "stm32f4xx_adc.h"

uint16_t adc_val[2];
uint8_t ch_index =0;



/*void SystemLibInits()
{

RCC_DeInit();

RCC_HSEConfig(RCC_HSE_ON);
while(RCC_WaitForHSEStartUp()!=SUCCESS)
RCC_PLLConfig(RCC_PLLSource_HSE,4,168,2,7);
RCC_PLLCmd(ENABLE);
FLASH_SetLatency(FLASH_Latency_5);
FLASH_PrefetchBufferCmd(ENABLE);
FLASH_DataCacheCmd(ENABLE);
FLASH_InstructionCacheCmd(ENABLE);

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(0x08!=RCC_GetSYSCLKSource());


} */

void SystemInits()
{
	unsigned int i;
    for (i=0;i<0x00100000;i++);          // OSC oturtma ve kurtarma rutini
    RCC->CFGR |= 0x00009400;         // AHB ve APB hizlarini max degerlere set edelim
    RCC->CR |= 0x00010000;            // HSE Xtal osc calismaya baslasin
    while (!(RCC->CR & 0x00020000));// Xtal osc stabil hale gelsin
    RCC->PLLCFGR = 0x07405408;      // PLL katsayilarini M=8, N=336, P=2 ve Q=7 yapalim
//  RCC->PLLCFGR = 0x07402A04;     // PLL katsayilarini M=4, N=168, P=2 ve Q=7 yapalim
    RCC->CR |= 0x01000000;            // PLL calismaya baslasin  (Rehber Sayfa 95)
    while(!(RCC->CR & 0x02000000)); // Pll hazir oluncaya kadar bekle
//  FLASH->ACR = 0x00000705;        // Flash ROM icin 5 Wait state secelim ve ART yi aktif edelim (Rehber Sayfa 55)
    FLASH->ACR = 0x00000605;        // Flash ROM icin 5 Wait state secelim ve ART yi aktif edelim (Rehber Sayfa 55)
    RCC->CFGR |= 0x00000002;        // Sistem Clk u PLL uzerinden besleyelim
    while ((RCC->CFGR & 0x0000000F) != 0x0000000A); // Besleninceye kadar bekle

}

/*********************************ADC KESME **********************************************************/

void ADC_IRQHandler()
{
if (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==SET) {
	ADC_ClearFlag(ADC1,ADC_FLAG_EOC);
	adc_val[ch_index]=ADC_GetConversionValue(ADC1);
	ch_index++;
	if (ch_index>1) {
		ch_index=0;
	}

}
}

/************************************************************************************************/
void GPIOD_Inits()
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);

	GPIO_InitTypeDef GPIO_InitStructure ;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT ;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Pin=(GPIO_Pin_15 | GPIO_Pin_14 | GPIO_Pin_13 | GPIO_Pin_12);
	GPIO_Init(GPIOD, &GPIO_InitStructure);


}


void rcc_gpio_config()
{

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);

	GPIO_InitTypeDef GPIO_B_InitStructure ;

	GPIO_B_InitStructure.GPIO_Mode=GPIO_Mode_AN;               // Anolog input
	GPIO_B_InitStructure.GPIO_Pin=(GPIO_Pin_0 | GPIO_Pin_1);   //PB0 PB1
	GPIO_B_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOB, &GPIO_B_InitStructure);


}
void ADC1_Inits()
{

    ADC_InitTypeDef ADC_InitStructure ;
    ADC_CommonInitTypeDef ADC_CommonStructure;

    ADC_CommonStructure.ADC_Mode=ADC_Mode_Independent;
    ADC_CommonStructure.ADC_Prescaler=ADC_Prescaler_Div4;
    ADC_CommonStructure.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonStructure.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled;
    ADC_CommonInit(&ADC_CommonStructure);


    ADC_InitStructure.ADC_Resolution=ADC_Resolution_12b;
    ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;
    ADC_InitStructure.ADC_ScanConvMode=ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion=2;
    ADC_Init(ADC1,&ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_480Cycles); //PBO
    ADC_RegularChannelConfig(ADC1,ADC_Channel_9,2,ADC_SampleTime_480Cycles); //PB1

    ADC_Cmd(ADC1,ENABLE);
    ADC_EOCOnEachRegularChannelCmd(ADC1,ENABLE);
    ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);

}

void NVIC_Inits()
{
	NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=ADC_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);

}


int main()
{

SystemInits();
GPIOD_Inits();
NVIC_Inits();
rcc_gpio_config();
ADC1_Inits();


ADC_SoftwareStartConv(ADC1);

while(1)
      {

	if (adc_val[0]>1500) {
		GPIO_SetBits(GPIOD,GPIO_Pin_15);
	}
	else {
		GPIO_ResetBits(GPIOD,GPIO_Pin_15);
	}

      }

}

2 kanalın verilerini okumaya çalışıyorum ama overrun bayrağı set oluyo veriyi kaybediyorum ve program kesmeye girmiyo.Bu problemi nasıl aşabilirim ?

muhittin_kaplan

Hocam şuraya Açıklık Getirirmisiniz
İki ADC yimi kullanmak istiyorsunuz 2 PIN (PB0 ve PB1)den okumamı yapmak istiyorsunuz.

Enginar

Evet 2 çevrim yapmak istiyorum while içinde bi tanesini okuduğuma bakmayın hocam debugdan gözlemliyorum 2 kanalıda ayrı çıkışlara bağlayıp.Sorunum çevrim yaparken overrun flagının set olması  ???

muhittin_kaplan

Hocam Malum üç adet ADC modülü var. siz her bir pini bu ADC modüllerine bağlamışsınız.
Hiç 2. Modülü kullanmadım. Bir Modülde 2 kanal kullandım.



Enginar

Evet hocam ADC1 modülünü kullanıyorum sadece .2 kanal tarayacağım ADC1 üzerinden scan ve continious conversion modda.Klein hocanın yaptığı örnekten yola cıktım. swstart bitini 1 yaptıktan sonra kesme geldikçe taranan kanalları adc_val dizisinde saklayacağım .kesme olmadan yaptım sıkıntı yok ama bu şekilde denediğimde veriyi okuyamadan program çakılıyo :-\


Klein

2 kanal continious modda kesme olmadan okuyamazsın. Daha ilk çevirimin verisini okumadan ikinci çevirimin verisi gelir Overrun bayrağı çekilir.

Interrup  pending bitini temizlemeyi dener misin?

Enginar

Evet o biti temizleyince stabil çalıştı program hocam teşekkür ederim.Sonradan o satırı kaldırıp derledim gene çalıştı :) garip :) Bu bitin görevi tam olarak nedir hocam ? anladığım kadarıyla kesme bekleyen biti temizlemek için kullanılıyo. mesela ovr set oldu ama kesmesi acık değil bu yüzden kesmeye gidemiyo bu biti temizlemek için kullanılıyo ?

Klein

Evet kesme bayrağını temizliyor.
Benim önerim ADC'yi DMA ile kullanman yönünde olur.
ADC çok hızlı örneklediği için, program büyüyünce ADC kesmesini işletmekten,  programı işletmeye zaman bulamıyosun. 

muhittin_kaplan

Burada benimde takıldığım noktalar var.

407 için konuşursam. 3 adet ADC modülü var neden ?

Klein

Her ADC'nin birden fazla kanalı var. Ancak bu kanalların hepsini aynı anda okuyamıyoruz. Belirli bir sıra ile peş peşe okuyabiliyoruz. Bu bir çok uygulama için yeterli. 
Ama düşün ki 3 fazlı bir sürücü yapıyorsun  eş zamanlı olarak tüm fazların akım veya voltaj bilgisine ihtiyacın var. Eğer tek ADC olursa kanalları sırasıyla okuyacaksın ve aldığın bilgiler eş zamanlı olmayacak. Bu da veriyi işlemenive kontrolünü zorlaştıracak.

muhittin_kaplan

Anladım. Eş Zamanlı Okuma Yapacağımızda Kullanabiliriz.

Hocam Yoruyorum Ama

Şu Modlardan Bahsedebilirmisiniz Biraz. Odc Okuma Modları.

Klein

#11
Single conversion, continious conversion , scan mod ve discontinious olmak üzere 4 çevirim modu var.

Scan modunda  iken:
çevirimi başlattığınızda ADC_SQRx  registeri ile bildirdiğiniz sayıda kanalı , aynı register ile bildirdiğiniz sırada tarıyor. her çevirimden sonra değeri registere atıyor ve conversion tamam bayrağını çekiyor. Tüm kanallar taranana kadar işlem böyle devam ediyor.
Tüm kanallar bittikten sonra, eğer Single conversion modundaysak duruyor. Takrar için yeniden çevirim başlatmamız gerekiyor. Continious conversion modundaysak, bizim yeniden çevirimi başlatmamıza gerek olmadan , çevirim kendi kendine yeniden başlıyor.

Scan modunda değilsek :
ADC_SQRx registerinde seçilmiş ilk kanal hangisi ise o kanalın verisinialıyoruz. Başka bir kanal seçeceksek, conversion bittikten sonra ADC_SQRx registerine Her zaman aynı kanalın bilgisini kesintisiz olarak alıyoruz. Continious modda iken kanal değiştirmek istersek, çevirim bittikten sonra, çevirimi durdurup kanal değiştiriyoruz. Çevirimi tekrar başlatıyoruz.


Discontinious mod scan modu gibi. Ancak biraz farklı. Yine tarama yapılıyor. Ancak tarama alt guruplara bölünüyor.
Diyelim ki   sıralamamız 1,3,2,7,9,4,2,5,8,10,11,5,6,9
taranacak kanal sayısı 4:
ADC_CR1 ->DISCNUM registerinden çevirimi her başlatmada kaç kanal taranacağını bildiriyoruz.
Örneğin bu değerimiz 2 olsun.
çevirimi başlattığımızda ilk iki kanal (1,3) taranıyor.
çevirimi tekrar başlattığımızda  ikinci iki kanal (2,7)
bir sonraki çevirimde (9,4)
bir sonrakinde (2,5)
kanallar taranıyor.
ADC_SQRx registerinde tarama sayısına 4 verdiğimiz için. 4. taramanın sonunda EOC(End of conversion) bayrağı kaldırılır.
Çevirimi tekrar başlattığımızda yine ilk sıradan çevirime başlar.

Bu modda çevirimi elle mi başlatıyoruz, başka kaynaklardan mı başlatmak gerekiyor tam emin değilim. Muhtemelen DMA gibi tetikleyiciler tarafından başlatılan çevirimleri için düşünülmüş bir özellik.

EMP_Otto

#12
Merhabalar ADC ile benimde bir sorum olacak :)
Vref+ ve Vref- voltajları ne acaba?Yani kaç volt.Bir türlü bulamadım :(
Bu işler zordur,özveri ister...

EMP_Otto

Ben sordum cevabınıda ben veriyim.
Hard dökümanı sayfa 63(hard dokumanı:Doc ID 022152 Rev 2)
Vref+  Vdd ye Vref- de Vss ye bağlı.Vdd=3.3 V Vss=0 :)
Bu işler zordur,özveri ister...

camby

Daha doğrusu küçük kılıflarda dışarı çıkarmıyorlar , direk beslemelere bağlıyorlar. Büyük paketlerde ise dışarı çıkıyor. Şöyle de bir sonuç çıkıyor aslında , vref kullanmak istiyorsak büyük paket kullanmak zorundayız.