Bölüm – IV
Bir yeni bölüm de daha birlikteyiz. Bugünkü bölümde iki kısımda; yeni iki donanım inceleyeceğiz. Birisi ADC, diğeri de ivme sensörü.
ADC, genelde mcu içine gömülüdür. Ancak farklı uygulamalar içerisinde kullanmak için üretilmiş, ADC entegreleri de mevcuttur. Bugün dahili ADC birimi ile bir kaç örnek yapacağız.
Diğer donanım ivme sensörü; çoğunlukla harici bir donanımdır. SPI/I²C gibi donanımlar ile mculara bağlanmaktadır.
Yapacağımız diğer örnek ivme sensörünü, eğim sensörü gibi kullanmaya çalışacağız. Eğimde oluşan ivme değerini ledlere bağlayacağız. Buradaki amacımız farklı donanımlar ile örnekler yapıp kullanımlarını öğrenmemiz.
Bir çok mcu içinde artık neredeyse olmazsa olmaz ADC birimi ile başlayalım. Epeyce eski bir birim olduğundan hem donanım içerisinde, hem de yazılımlar içerisinde okuma çok basitleştirilmiştir. Ancak bunun yanında; gelişen mcu teknolojisi ile birlikte daha kapsamlı yapılandırmalar da bulunmaktadır.
STM32F407V içerisinde 12-bit, 16 kanal(0-15) A/D çevirici bulunmaktadır. Ben daha önceki çalışmalarım için yaptığım, dört kanal pot modülünü kullanacağım.
Bu basit proje, EasyEDA ile çizildi ve https://oshwlab.com/mehmetbey/4ch-pot_01 linkinde paylaştım.
Bu mcuda bir de; sıcaklık sensörü, Vrefint ve Vbat bulunmakta 16, 17 ve 18. kanal olarak okunuyor. Sıcaklık sensörü, çok hassas olmadığı kataloğunda yazmaktadır. Bunu sadece sıcaklık değişimlerinin takibi için olduğu belirtilmektedir. Bir örnek de bunlar için yapacağız.
ADC tanımlamasını ve basit bir örnek yapalım. Discovery board P1; 7-10 pinleri ADC 10-13 pinlerine bağlıdır. Bu pinler PC0-PC3 arasındaki 4 pindir. Alttaki kodu terminale yazalım. PC0 ‘a bağlı olan potu ora bir noktaya getirelim. 12-bit değer okuyacağız. Bu durumda; pot orta noktada iken yaklaşık 2048 değerini görmemiz gerekmektedir.
1 2 3 4 5 |
#-------------------------------------------------------- import pyb adc = pyb.ADC(pyb.Pin("PC0")) adc.read() |
Yaklaşık bu değeri gördüysek sorun yok. Ancak göremediysek, pin bağlantılarını ve kodlarımızı gözden geçirelim.
Aynı şekilde diğer pot kanallarını da deneyebiliriz. Alttaki kodlar ile dört kanalı devamlı okuyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#-------------------------------------------------------- import pyb """ PC0 -> ADC_IN10 PC1 -> ADC_IN11 PC2 -> ADC_IN12 PC3 -> ADC_IN13 """ #farklı tanımlamalar ile yaptım, tüm kullanım da doğrudur. adc1 = pyb.ADC(10) #PC0 adc2 = pyb.ADC(pyb.Pin("PC1")) adc3 = pyb.ADC(pyb.Pin.board.PC2) adc4 = pyb.ADC(pyb.Pin.cpu.C3) print("\nDurdurmak için 'Ctrl + C' ye basınız.\n") try: while True: deger1 = adc1.read() deger2 = adc2.read() deger3 = adc3.read() deger4 = adc4.read() print(f"ADC ch 10 = {deger1:04d}, ADC ch 11 = {deger2:04d}, ADC ch 12 = {deger3:04d}, ADC ch 13 = {deger4:04d}") pyb.delay(100) except KeyboardInterrupt: print("\nKlavye kesmesi tespit edildi.\nProgram sonlandırıldı.\n") |
Basit uygulamalarda ADC.read() metodu işe yarar. Ancak bazı sensör uygulamalarında, gerilim izleme uygulamaları, vb. uygulamalarda; okunan değerler yazılımsal filtrelerden geçirilmesi gerekmekte. Bu gibi durumlarda farklı okuma işlemleri yapılabilir. Örneğin, belli zamanlarda örnek alıp bir diziye aktarılır. Daha sonra bu dizi istenilen şekilde değerlendirilebilir. Şimdiki örneğimizde sadece terminale yazdıracağız. Siz istediğiniz şekilde kullanabilirsiniz; ortalaması alınabilir, medyan fitre uygulanabilir, vs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#-------------------------------------------------------- import pyb print("\nDurdurmak için 'Ctrl + C' ye basınız.\n") adc = pyb.ADC(pyb.Pin.board.PC0) tim = pyb.Timer(6, freq=10) # Bir timer istenilen frekansa kurulur buf = bytearray(100) # burada örnek dizi boyutu 100 değer try: adc.read_timed(buf, tim) # 10 saniye iöerisinde 100 değer oku for i in buf: print(i) except KeyboardInterrupt: print("\nKlavye kesmesi tespit edildi.\nProgram sonlandırıldı.\n") |
Üstteki örnekte buf dizisi 8-bit. Okunan 12-bit değer, 8-bite dönüştürülerek diziye aktarılır.
Eğer 12-bit değerlere ihtiyacımız varsa array modülü kullanarak yapabiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#-------------------------------------------------------- import pyb, array # Analog pin hazırlama adc1 = pyb.ADC(pyb.Pin.board.PC0) adc2 = pyb.ADC(pyb.Pin.board.PC1) adc3 = pyb.ADC(pyb.Pin.board.PC2) adc4 = pyb.ADC(pyb.Pin.board.PC3) tim = pyb.Timer(8, freq=100) # Bir timer istenilen frekansa kurulur # Her kanal için ayrı dizi tanımlıyoruz rx1 = array.array('H', (0 for i in range(100))) # ADC dizileri rx2 = array.array('H', (0 for i in range(100))) # 100 adet, 16-bit rx3 = array.array('H', (0 for i in range(100))) rx4 = array.array('H', (0 for i in range(100))) # 10mS de birer değer okunarak kendi dizilerine aktarılır. pyb.ADC.read_timed_multi((adc1, adc2, adc3, adc4), (rx1, rx2, rx3, rx4), tim) for n in range(len(rx1)): print(f"{int(n):03,.0f}. ADC1 = {int(rx1[n]):04,.0f}, ADC2 = {int(rx2[n]):04,.0f}, ADC3 = {int(rx3[n]):04,.0f}, ADC4 = {int(rx4[n]):04,.0f}") |
ADC içerisinde bir de sıcaklık sensörünün olduğundan bahsetmiştik. Bunu okutabilmek için pyb.ADCAll modülü kullanılmaktadır. Aslında normal ADC kanallarını da okumak mümkün.
Bu modül tanımlanırken; hangi kanalların ADC olarak okunacağı(mask) ve çözünürlüğü belirtilmektedir. Kullandığımız mcuda 16 kanal(0-15) ADC birimi bulunmakta idi. Ancak 16-18 arası bitlerin sıcaklık sensörü, Vrefint ve Vbat olduğunu söylemiştik. dolayısıyla mask değerinde bu 3-bitin de eklenmesi gerekmekte.
Biz bu örneğimizde şimdilik, sadece bu üç değeri okuyalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#-------------------------------------------------------- import pyb # ADC Bit maskesi # 3322 2222 2222 1111 1111 1100 0000 0000 # 1098 7654 3210 9876 5432 1098 7654 3210 # 0000 0000 0000 0111 0000 0000 0000 0000 burada dahili 16,17 ve 18 isteniyor # hex olarak 0x070000 degeri oluyor. # -> Çözünürlük(burada 12-bit) # | -> mask # | | adc = pyb.ADCAll(12, 0x070000) #12-bit 16-17-18 #hard ressetten sonra bir kac kere okumak gerekiyor. coreTemp = adc.read_core_temp() coreVbat = adc.read_core_vbat() coreVref = adc.read_core_vref() vref = adc.read_vref() coreTemp = adc.read_core_temp() coreVbat = adc.read_core_vbat() coreVref = adc.read_core_vref() vref = adc.read_vref() print(f"\nIC sıcaklığı {coreTemp:4.1f}°C dır.") print(f"IC Vbat {coreVbat:4.2f}V.") print(f"IC Vref {coreVref:4.2f}V.") print(f"Vref {vref:4.2f}V.\n") |
Daha önce de söylemiştik; sıcaklık sensörü hassas değil. Sadece sıcaklık değişimlerini gözlemlemeye yarıyor.
Şimdilik ADC konusu bu kadar, gerisi sizin hayallerinize kalmış. Bol bol örnek yapınız.
Discovery borduna harici olarak bağlı olan donanımlardan birisi de ivme sensörü. Benim elimdeki kart; MB997C. Bu model üzerindeki sensör LIS3DSH. Buradaki linkte Discovery kart üzerindeki ivme sönsörlerine uygun yazılmış modül bulunmakta. Bu bu modülü; staccel.py adıyla flash alanına kaydedelim. Ardından alttaki uygulamayı yazalım:
1 2 3 4 5 6 7 8 9 10 |
#-------------------------------------------------------- import staccel import pyb ac = staccel.STAccel() while(1): print(f"x = {ac.x():9.6f}, y = {ac.y():9.6f}, z = {ac.z():9.6f}") #print(ac.xyz(), ac.t()) pyb.delay(300) |
Kartı hareket ettirdiğimizde ekrandaki değerlerin değiştiğini görmekteyiz. Bu haliyle en fazla ±2g değer üretebiliyor. (1g = 9.8m/s²)
Bu sensör 16g ‘ye kadar değer üretebilmekte. Eğer siz isterseniz; staccel.py dosyasını; LIS3DSH sensörünün belgelerine bakarak yeniden düzenleyebilirsiniz. Böylece istediğiniz ivme değerlerini almanız mümkün olacaktır.
Ben bu modülde bir değişiklik yaptım. Modüle erişildiğinde LIS3DSH yada LIS302DL ivme sensörünün bulunduğunu terminale yazıyor. staccel.py dosyasının 58. satırından sonra bir boşuk ekleyin ve 57. satırdaki else hizasında, 58. satıra alttaki kodu ilave edin. print("\nLIS3DSH bulundu.\n" if (self.who_am_i == 0x3F) else "\nLIS302DL bulundu.\n")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class STAccel: def __init__(self): self.cs_pin = Pin("PE3", Pin.OUT_PP, Pin.PULL_NONE) self.cs_pin.high() self.spi = SPI(1, SPI.MASTER, baudrate=328125, polarity=0, phase=1, bits=8) self.who_am_i = self.read_id() if self.who_am_i == LIS302DL_WHO_AM_I_VAL: self.write_bytes(LIS302DL_CTRL_REG1_ADDR, bytearray([LIS302DL_CONF])) self.sensitivity = 18 elif self.who_am_i == LIS3DSH_WHO_AM_I_VAL: self.write_bytes(LIS3DSH_CTRL_REG4_ADDR, bytearray([LIS3DSH_CTRL_REG4_CONF])) self.write_bytes(LIS3DSH_CTRL_REG5_ADDR, bytearray([LIS3DSH_CTRL_REG5_CONF])) self.sensitivity = 0.06203 * 256 #0,06 * 256 else: raise Exception("LIS302DL or LIS3DSH accelerometer not present") print("\nLIS3DSH bulundu.\n" if (self.who_am_i == 0x3F) else "\nLIS302DL bulundu.\n") |
Şimdi son olarak ivme sensörünün değerine bağlı olarak kart üzerindeki ledleri; pwm duty değerine bağlı olarak yakan bir uygulama yazalım. Bu örnekte X/Y düzlemindeki değerlere göre ledlerin duty değerine bağladım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#-------------------------------------------------------- import staccel import pyb ac = staccel.STAccel() sw = pyb.Switch() greenLed = pyb.Pin('LED_GREEN') #"PD12" de yazılabilir. orangeLed = pyb.Pin('LED_ORANGE') #"PD13" de yazılabilir. redLed = pyb.Pin('LED_RED') #"PD14" de yazılabilir. blueLed = pyb.Pin('LED_BLUE') #"PD15" de yazılabilir. # STM32F DISCO kartındaki ledler PD12-PD15 arasındadır. # Bu dört pin TMR4 içindeki dört kanala bağlıdır. tim4 = pyb.Timer(4, freq=1000) gLed = tim4.channel(1, pyb.Timer.PWM, pin=greenLed) oLed = tim4.channel(2, pyb.Timer.PWM, pin=orangeLed) rLed = tim4.channel(3, pyb.Timer.PWM, pin=redLed) bLed = tim4.channel(4, pyb.Timer.PWM, pin=blueLed) gLed.pulse_width_percent(0) oLed.pulse_width_percent(0) rLed.pulse_width_percent(0) bLed.pulse_width_percent(0) # Bu iki aralık içindeyse zemini düz kabul ediliyor. MIN_NEG = -0.015 MIN_POS = 0.015 x_fark = 0.0 y_fark = 0.0 x = 0.0 y = 0.0 def duzlem_sifirla(): xFark = ac.x() yFark = ac.y() return xFark, yFark #------------------------------------------------------------- # Düzlem sensörüne verilerine alçak geçiren filtre eklenecek. #------------------------------------------------------------- try: while(1): if(sw.value): pyb.delay(100) if(sw.value()): x_fark, y_fark = duzlem_sifirla() print("\nDüzlem sıfırlandı.\n") print(x_fark, y_fark) x = ac.x() - x_fark y = ac.y() - y_fark if(x < MIN_NEG): rLed.pulse_width_percent(0) gLed.pulse_width_percent(abs((x) * 100)) print("sol") elif((x) <= MIN_POS and x >= MIN_NEG): rLed.pulse_width_percent(0) gLed.pulse_width_percent(0) print("orta X") elif(x > MIN_POS): rLed.pulse_width_percent(x * 100) gLed.pulse_width_percent(0) print("sag") if(y < MIN_NEG): oLed.pulse_width_percent(0) bLed.pulse_width_percent(abs(y * 100)) print("ileri") elif(y <= MIN_POS and y >= MIN_NEG): oLed.pulse_width_percent(0) bLed.pulse_width_percent(0) print("orta Y") elif(y > MIN_POS): oLed.pulse_width_percent(y * 100) bLed.pulse_width_percent(0) print("geri") pyb.delay(100) except KeyboardInterrupt: gLed.pulse_width_percent(0) oLed.pulse_width_percent(0) rLed.pulse_width_percent(0) bLed.pulse_width_percent(0) tim4.deinit() pyb.Pin(pyb.Pin.board.PD12, mode=pyb.Pin.OUT) pyb.Pin(pyb.Pin.board.PD13, mode=pyb.Pin.OUT) pyb.Pin(pyb.Pin.board.PD14, mode=pyb.Pin.OUT) pyb.Pin(pyb.Pin.board.PD15, mode=pyb.Pin.OUT) print("\nKlavye kesmesi tespit edildi.\nProgram sonlandırıldı.\n") |
Benim masada yaklaşık 0.15 değer civarında hata veriyor, muhtemelen masa bir kaç derece eğimli. <USER> buton ile bu değerin sıfırlanmasını sağladım.
Bugünkü biraz uzun bir yazı oldu. Umarım eğlenceli olmuştur. micropython ve Discovery kartı hakkında biraz daha fazla bilgimiz olduğunu düşünüyorum. Aklınıza takılan yerleri; “Hakkımda” alanından ulaşabileceğiniz bir posta adresim bulunmakta. Bilgim dahininde cevaplamaya çalışacağım.
İyi eğlenceler…
7 segment 7segment 12F675 12F683 ADC Arduino AVR AvrDude BUTON button Code Composer Studio Direnç esp esp8266 G2552 GLCD itoa led LIS3DSH LIS302DL micropython MPLABX MSP430 ortanca filtre pendulum PIC PIC18F452 port PWM PySimpleGUI python pyttsx3 seven segment STM32 STM32F-Discovery STM32F4-DISCO STM32F4-Discovery STM32F407 Thonny Timer0 usb Voltmetre XC8 Zener test cihazı İvme Sensörü