STM32F4 StdPeriph ve DMA ile Çok Kanallı ADC Okuma

STM32F4 StdPeriph ve DMA ile Çok Kanallı ADC Okuma

Herkese merhabalar, bu yazıda ARM tabanlı STM32F4 mikro işlemci ailesinde çok kanallı ADC okuma işlemini DMA ile nasıl yapildığını anlatacağım. Bu uygulamada STM32F429ZI nucleo geliştirme kartı kullanacağım. Programımızı ile Std Periph Kütüphanesi kullanarak uVision Keil aracılığı ile yazacağım.

Hadi başlayalım.

İlk olarak Initialization yapılarını oluşturalım. Burada GPIO, DMA ve ADC için gerekli tanımlamaları yapacağız. Bu tanımlamaları yaptıktan sonra, ilgili donanımların çalıştığı busları aktifleştirmemiz gerekmektedir. Aşağdaki kod bloğunda her ikisininde nasıl yapıldığını görebilirsiniz.


ADC_InitTypeDef       ADC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;
DMA_InitTypeDef       DMA_InitStruct;
GPIO_InitTypeDef      GPIO_InitStruct;

//Gerekli busları aktifleştirme
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA2 , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);

Şimdi ilk olarak GPIO yapılandırmasını yapalım. GPIO yapılandırması ile uygulamada kullanılacak olan pinlerin yapılandırmalarını oluşturacağız.

Aşağıdaki kod bloğunda GPIO yapılandırmasını görebilirsiniz.


GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
//GPIO pinlerini Analog Input olarak ayarladık
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ;
//Yukarıdaki ayarlar ile ve pinleri GPIOA ile başlat
GPIO_Init(GPIOA, &GPIO_InitStruct);

ADCsi okunan kanalların adc değerlerini bir dizide tutmamız gerekmektedir. Aşağıdaki ADCValues isimli ve 6 elemanlık dizide bu işlemi gerçekleştireceğiz. Aynı zamanda DMA için aşağıdaki değişkeni hedef değer olarak tanımlayacağız.


uint16_t ADCValues[6] = {0,0,0,0,0,0};

Şimdi sıra geldi DMA yapılandırmalarına. Aşağıdaki kod bloğunda DMA donanım yapılandırmasını yapacağız.

Gerilim değerlrini okumak için ADC2 donanımını kullanacağız. ADC2 donanımı DMA2 Channel1 Stream kanalına bağlıdır. Dolayısıyla bizde DMA2 aktifleştireceğiz ve mode PeriphalToMemory seçeceğiz. ADC2 donanımında bellekte belirlenen değişkene veri aktarmış olacağız.

    //Kanalı DMA_Channel_1 seçtik
    DMA_InitStruct.DMA_Channel = DMA_Channel_1;
    //Veri kaynağını ADC2 nin data sını ve Memory ide ADCValues değişkeni yaptık.
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC2->DR;
    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&ADCValues;
    //Veri akış yönünü  Periphal to Memory seçtik.
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
    //ADC nin 6 kanalını okuyacağımız için buffer size değerini 6 yaptık.
    DMA_InitStruct.DMA_BufferSize = 6;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //16-bitlik veriler okuyacağız ve yazacağız. Dizi değişkenide zaten 16 bitlik değişkendi.
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    //Circular mode ile aktarım bittikten sonra otomatik olarak yeniden başlayacak.
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    //DMA yı yapılandırmasını oluşturdul
    DMA_Init(DMA2_Stream2, &DMA_InitStruct);
    //DMA yı aktifleştirdik.
    DMA_Cmd(DMA2_Stream2, ENABLE);

DMA'mız hazır ve şimdi artık ADC yi hazırlamamız gerekmekte.


    ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div2;
    ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&ADC_CommonInitStruct);
   
    //İlk olarak ADC yi resetlemek için bir DeInit yapalım.
    ADC_DeInit();
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_8b;
    //ADC yi çok kanallı okumak için aktifleştiriyoruz
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;
    //Ve sürekli örnekleme yapsın olarak ayarlıyoruz
    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStruct.ADC_ExternalTrigConv = 0;
    ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    //Kaç tane analog veri okuyacağımızı belirtiyoruz
    ADC_InitStruct.ADC_NbrOfConversion = 6;
    //Ve yukarıdaki yapılandırmalar ile ADC yi başlatıyoruz.
    ADC_Init(ADC2, &ADC_InitStruct);

ADC ve DMA başlatıldığına göre gerekli ADC kanallarını belirtmemiz gerekli. Aşağıda ADC kanallarının bağlı olduğuu GPIO pinlerinin listesi bulunmakta. Bu tablo STM32F4 datasheetten alınmıştır. Detaylı bilgi için datasheet i incelemeniz gerekmektedir.

  • PA2: ADC2 Channel_2
  • PA3: ADC2 Channel_3
  • PA4: ADC2 Channel_4
  • PA5: ADC2 Channel_5
  • PA6: ADC2 Channel_6
  • PA7: ADC2 Channel_7

Şimdi aşağıda okuma sırasını belirtmemiz gerekmektedir. Aynı zamanda birim zamandaki örnekleme sayısınıda bu kod bloğunda belirtiyoruz.


    ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_144Cycles);//PA2
    ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 2, ADC_SampleTime_144Cycles);//PA3
    ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 3, ADC_SampleTime_144Cycles);//PA4
    ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 4, ADC_SampleTime_144Cycles);//PA5
    ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 5, ADC_SampleTime_144Cycles);//PA6
    ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 6, ADC_SampleTime_144Cycles);//PA7

Daha sonra ADC yi başlatıyoruz.


    //ADC yi aktifleştir
    ADC_DMARequestAfterLastTransferCmd(ADC2, ENABLE);
    ADC_DMACmd(ADC2, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
    //ADC okumasını başlat.
    ADC_SoftwareStartConv(ADC2);

İşlemimizi tamamladık. Fakat bir konuyu belirtmek isterim

Debug ekranında değerleri izlerken muhtemelen voltaj değişim değerlerini göremeyeceksiniz. Ben kendi uygulamamda göremedim. Bu nedenle basit bir len blinking uygulaması ile yukarıdaki işlemleri takip edebilirsiniz.

Aşağıda basit bir led blinking uygulaması kodları bulunmakta. STM32F429ZI Nucleo boardunda B14 pini LD3'teki Kırmızı lede bağlı durumdadır. ADC den okunan değere göre ledin yanıp sönme hızını belirleyen kodları aşağıdaki kod bloğunda bulabilirsiniz..


RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
//GPIO yapılandırmasını aktifleştir
GPIO_Init(GPIOB, &GPIO_InitStructure);
				
    while(1) 
    {
		GPIO_SetBits(GPIOB, GPIO_Pin_14);
		fnc_delay(ADCValues[0] * 50000);
		GPIO_ResetBits(GPIOB, GPIO_Pin_14);
		fnc_delay(ADCValues[0] * 50000);
    } 

Yazıda gördüğünüz üzere herhangi bir NVIC interrupt veya bir while döngisi içerisinde ADC nin değerlerini okuma yapmadık ve otomatik olarak değişkene atanmış oldu. DMA nın kullanım amacını bu şekilde anlatmış olduk.

Bu yazımızda bu kadar.

İyi çok kanallı ADC okumalar.

Burak hamdi TUFAN


Tags


Share this Post

Send with Whatsapp

Post a Comment

Success! Your comment sent to post. It will be showed after confirmation.
Error! There was an error sending your comment. Check your inputs!

Comments

  • There is no comment. Be the owner of first comment...