DMA ile ADC Okuma ve Filtreleme Gerçek dünyadaki analog bilgilerin işlenebilmesi için önce sayısal verilere dönüştürülmesi gerekir. Bu sayısal veriler de kullanılabilir hale getirmek için önce filtrelenmeli ardından kalibre edilmelidir. Bu dönüştürme ve filtreleme işlemleri sırasında çok sayıda örnek alınmalı ve işlemcinin bir miktar işlem gücünü harcayarak, örnekler üzerinden hesaplama yapılmalıdır. Alternatif bir yöntem olarak, işlem gücü harcamadan, DMA ile paralelde örnekleme ve filtreleme yapabiliriz. Ek bir fayda olarak, sayısal verilerimizi bir yandan filtrelerken, bir yandan çözünürlüklerini arttırabiliriz. Bu dökuman da işlem gücünüzü harcamak yerine DMA yı nasıl kullanabileceğinizi, ve okunan verilerin çözünürlüğünü nasıl arttırılabileceğini STM32F100 mikrodenetleyicisi ile anlatacağız. 11.Nis.18 www.cmb-tech.com 1/9
İçindekiler Analog Verilerin Sayısallaştırılması...3 STM32F100 ile Sayısallaştırma...3 Bağlantılar...4 Verilerin Okunması...4 DMA Nedir?...7 DMA Konfigurasyonu...7 Çözünürlüğün Arttırılması...8 Sıra Sizde...9 11.Nis.18 www.cmb-tech.com 2/9
Analog Verilerin Sayısallaştırılması Günümüzde hemen hemen tüm mikrodenetleyici içinde bulunan ADC (Analog-Digital Converter), dış ortamdaki analog verileri bir sayısal değere dönüştürür. Önce, dış ortamdaki ölçülecek değerler (ısı, ışık, ivme, basınç vb.) bir algılayıcı (sensör) ile elektriksel gerilime (frekans veya darbe -pulse- de olabilir) dönüştürülür. İşte bu analog gerilimin mikrodenetleyici içerisinde işlenebilecek şekilde sayısal değere dönüştürülmesi ADC tarafından gerçekleştirilir. Bu aşamadan sonra sayısal veri üzerinde çalışılabilir. Bir analog gerilimi, sayısal değere dönüştürdüğümüzde aslında, mevcut gerilim değeri üzerinden bir örnek almış oluruz. Fakat bu gerilim, üzerinde bir gürültü de barındırdığından, örnek aldığımız noktada sinyalin gerçek ortalama değerini bulabilmiş sayılmayız. Bunun için sinyalden çok sayıda örnek alıp, bu örnekleri filtrelememiz de gerekiyor. STM32F100 ile Sayısallaştırma STM32VLDiscovery kartı üzerinde STM32F100 mikrodenetleyicisi, bu mikrodenetleyici üzerinde de 12-bit lik, toplamda 16 adet ADC kanalı bulunmaktadır. Biz bunların 8 tanesini kullanarak, 8 kanal analog veri okuyacağız. Kullanacağımız analog ölçüm kanallarının bağlı olduğu pinler aşağıdaki tabloda verilmiştir. Kanal Pin 0 PA0 1 PA1 2 PA2 3 PA3 4 PA4 5 PA5 6 PA6 7 PA7 12-bit lik ADC ile 0~3.3V arasındaki gerilim değeri, 0~4095 arasında sayısal değerlere dönüştürülür. Buna göre, sayısal değerdeki her bir değişim, gerilim seviyesindeki 0.8mV değişime karşılık gelmektedir. 3.3V / 4096 = 0.8mV 11.Nis.18 www.cmb-tech.com 3/9
Bağlantılar Test için tüm kanallara, potansiyometreli bir gerilim bölücü üzerinden giriş veriyoruz. Verilerin Okunması ADC ile analog verilerin okunması kısmına girmeyeceğiz. Bununla ilgili çok sayıda örneğe internetten ulaşabilirsiniz zaten. Şimdilik sadece, ADC den okunan verileri, UART3->PB10 (115200,8n,1) üzerinden, ASCII formatında PC ye göndereceğiz. Böylece verileri kaydedebilecek ve inceleyebileceğiz. Proje kaynak kodlarına buradan ulaşabilirsiniz. Fark edeceğiniz gibi, henüz DMA kullanmaya başlamadık. Önce, DMA kullanmadan nasıl çalışıyoruz, sonra da DMA bize ne faydalar sağlayacak bunu göreceğiz. Aşağıda, PC ye gelen verileri görebilirsiniz. Sırasıyla 8 kanal veri virgül ile ayrılmış şekilde gönderilmektedir. 11.Nis.18 www.cmb-tech.com 4/9
Bu verilerin grafiğini çizersek, okunan değerlerde gürültülerin olduğunu göreceğiz. Veriler için minimum değer 2040 maksimum değer 2070. Buna göre gürültünün gerilim değeri; 0.8mV * ( 2070 2040 ) = 24mV olacaktır. Bu, uygulamada sürekli karşımıza çıkan ve çözmemiz gereken bir sorundur. Çözümler arasında en sık kullanılanı, ortalama alma yöntemidir. Yani örnek sayısı arttırılarak, bu örneklerin ortalama değeri filtrelenmiş çıkış olarak kullanılabilir. uint16_t ADC_GetData( uint8_t channel ){ uint8_t i; uint16_t retval = 0; for(i=0;i<adc_channel_data_count;i++){ retval += adcdata[channel][i]; } return retval/adc_channel_data_count; } Okunan verilerin grafiğini çizdiğimizde, gürültülerden büyük oranda kurtulduğumuzu görebiliriz. 11.Nis.18 www.cmb-tech.com 5/9
Veriler için minimum değer 2038 maksimum değer 2046. Buna göre gürültünün gerilim değeri; 0.8mV * ( 2046 2038 ) = 6.4mV olacaktır. Denemek isterseniz projenin mevcut haline buradan ulaşabilirsiniz. 11.Nis.18 www.cmb-tech.com 6/9
DMA Nedir? Doğrudan Bellek Erişimi (Direct Memory Access), çevresel birimlerden RAM belleğe, RAM bellekten çevresel birimlere veya RAM bellekten RAM belleğe veri aktarımını CPU kullanmadan yapan donanımdır. Örneğin, ethernet paketini oluşturdunuz ve bu paketi ethernet çevresel birimine göndermek istiyorsunuz. Eğer DMA nız varsa ve kullanmıyorsanız, 1500 byte civarında olabilecek bu veriyi sıradan yöntemler ile kopyalamak, CPU nun işlem gücünü boşa harcamanız anlamına gelecektir. Ethernet üzerinden alınan paketleri, çevresel birimden belleğe alırken de benzer durum oluşacaktır. Veya sadece RAM bellekteki uzun bir veriyi, başka bir RAM alanına kopyalamak istiyorsunuz. Bu durumda da DMA size yardımcı olmak için oradadır. Biz ise DMA yı, ADC okumalarının CPU yu yormadan gerçekleşmesi için kullanacağız. DMA Konfigurasyonu DMA yı kullanabilmek için öncelikle DMA nın saat sinyalini açmamız gerekiyor. RCC->AHBENR = 0x00000001;//DMA1 clock enable Daha sonra veri transferinin çevresel birimden RAM belleğe olacağını belirtiyoruz. DMA1->IFCR = 0x00000001; //Clear Interrupt flags DMA1_Channel1->CCR = 0x05A0;//Set source data length and direction Ardından, kaç byte veri transferi yapılacağını söylemeliyiz. DMA1_Channel1->CNDTR = ADC_CHANNEL_COUNT * ADC_CHANNEL_DATA_COUNT;//count DMA kullanmadığımız durumda, ADC datasının hangi adresten hangi adrese kopyalanacağını biliyor ve buna göre kodu yazıyorduk. DMA kullanırken bu bilgileri DMA ya bildirmeliyiz. DMA1_Channel1->CPAR = (uint32_t)&adc1->dr;//source address DMA1_Channel1->CMAR = (uint32_t)adcdata;//destination start address Son olarak DMA yı aktif ediyoruz. DMA1_Channel1->CCR = 0x0001;//Enable channel Bu konfigürasyonda ADC ye de Beni rahatsız etme, vekilim DMA yı kullan demek için; ADC1->CR2 = 0x000E7103; //Cont. Conversion mode, DMA enabled Artık, ADC her dönüştürme sonunda kesme üretmeyecek ve DMA yı tetikleyerek, sonuçları adcdata[][] değişkenine sırayla yazacak. Kesmelerden de kurtulduğumuz için CPU çevrim sonuçlarını kopyalamak veya yeni çevrim başlatmak için hiç zaman kaybetmeyecek. 11.Nis.18 www.cmb-tech.com 7/9
Şimdi sonuçlara tekrar bakalım; Görüldüğü gibi, bu yöntemde de filtrelenmiş veriyi elde ettik. Burada da gürültüyü hesaplarsak; 0.8mV * ( 2046 2038 ) = 6.4mV buluruz. Çözünürlüğün Arttırılması 12-bit lik bir ADC ile 0~4095 arasında bir sayısal değer alabileceğimizi söylemiştik. Bu da her bir sayısal değerin 0.8mV luk bir değişime karşılık gelmesi demekti. Bir adım daha ileri giderek, bu çözünürlüğün arttırılmasına çalışalım. Proje boyunca her kanalda 16 örnek topluyor ve bu örneklerin ortalamasını alıyorduk. Bunun yerine, 16 örneğin toplamını sonuç olarak kullanmayı deneyelim. Bu durumda 0~65535 arasında değer üreten 16- bit lik bir ADC sonucum olacaktır. uint16_t ADC_GetData( uint8_t channel ) { uint8_t i; uint16_t retval = 0; for( i = 0; i < ADC_CHANNEL_DATA_COUNT; i++ ) { retval += adcdata[i][channel]; } return retval; } 11.Nis.18 www.cmb-tech.com 8/9
Buna göre, minimum(32828) ve maksimum(32914) değerler arasındaki fark artmış görünüyor. Ancak, bu durumda her birime karşılık gelen gerilim değeri 0.8mV değil, 3.3V / 65536 = 0.05mV olacaktır. Buna göre bir hesap yaparsak; 0.05mV * (32914 32828) = 4.3mV buluruz. Dolayısıyla çözünürlüğümüz 16-bit olduğu halde, gürültü -beklediğimiz gibi- artmamıştır. Sıra Sizde Projenin kaynak kodlarına buradan ulaşabilirsiniz. Peki şimdi neler yapılabilir? 16-bit lik sonuçlar üzerindeki gürültüyü filtreleyebilirsiniz. 16-bit veya 12-bit lik sonuçlar üzerinde bir alçak/yüksek geçiren filtre uygulayabilirsiniz. 11.Nis.18 www.cmb-tech.com 9/9