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.
//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.
Ş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
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
Comments