16F88 adc kesmesi owerflow hatası.

Başlatan greatgonzo, 21 Mart 2012, 15:38:29

greatgonzo

Son bir kaç gündür 16F88 için adc kesmesi oluşturmaya çalışıyorum. Bunu yaparkende @sleep komutunu kullanarak normal şartlarda işlemciyi uyutmak adc değişikliğindede uyandırıp lcd ye ilgili bilgileri yollamak ve tekrar sleep moda sokmak istiyorum. Acaba adc kesmesini yanlışmı yorumluyorum. Bu kesme adc değişikliği durumundamı oluşuyor (yani bacağa gelen sinlayin var/yok durumu değilde değerin değişmesi), yoksa adc okumasından sonra okuma bitti bayrağıyla birliktemi reset vektörüne sıçrıyor. Bir diğer yaklaşımında ki bunu denemedim. Acaba uyanma işini komparatörle yapıp adc ondan sonra faal olsa mantıken nasıl olur. Kodumu aşağıda veriyorum.

İyi çalışmalar.

@ DEVICE PIC16F88
@ DEVICE PIC16F88, WDT_ON
@ DEVICE PIC16F88, PWRT_ON
@ DEVICE PIC16F88, PROTECT_OFF
@ DEVICE PIC16F88, MCLR_OFF
@ DEVICE PIC16F88, INTRC_OSC_NOCLKOUT
@ DEVICE PIC16F88, LVP_OFF
@ DEVICE PIC16F88, BOD_ON
@ DEVICE PIC16F88, FCMEN_OFF
@ DEVICE PIC16F88, IESO_OFF
@ DEVICE PIC16F88, CCPMX_OFF
@ DEVICE PIC16F88, DEBUG_OFF
@ DEVICE PIC16F88, WRT_OFF
@ DEVICE PIC16F88, CPD_OFF

DEFINE OSC 4
DEFINE ADC_BITS 10
DEFINE ADC_SAMPLEUS 50

DEFINE LCD_DREG PORTB
DEFINE LCD_DBIT 0
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 4
DEFINE LCD_RWREG PORTB
DEFINE LCD_RWBIT 5
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 6
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2

OPTION_REG.7=0
OSCCON=%01101100
CMCON=7
ANSEL=%00000011
TRISA=%00000001
TRISB=%00000000
PORTA=0
PORTB=0
ADCON0=%11001000
ADCON1=%10000000

INTCON.7=1   'genel interruptları açılıyor.
INTCON.6=1   'peripheralinterruptları açılıyor.
PIE1.6=1     'ADC interrupt enable bit açılıyor.
PIR1.6=0     'ADC interrupt bayrağı sıfırlanıyor.

LCDOUT $FE,1
PAUSE 200
MSJ VAR word
MSJJ VAR word

ON INTERRUPT GOTO KESME

BASLA:
LCDOUT $FE,1,"LCDFAAL"
LCDOUT $FE,$C0,#MSJ," ",#MSJJ
@SLEEP

disable
KESME:
INTCON.7=0 'Genel kesmeler kapatılıyor.
ADCIN 0,msj
pauseus 100
ADCIN 1,MSJJ
pauseus 100
PIR1.6=0  'ADC interrupt bayrağı sıfırlanıyor.
INTCON.7=1 'Genel kesmeler kapatılıyor.
resume
enable

GOTO BASLA
END


mg1980

Hocam,
Buna cecap vermek bana düşmez ama kimse yazmayınca ben bildiğim basit bir hususu yazmak istedim.Yanlışsa doğrusunu birlikte öğrenme fırsatımız da olur böylelikle.Anladığım kadarıyla bu
bildiğimiz RB0, RB4-7, USART kesmelerine uymuyor. Siz A0'dan ADC sinyali uyguluyorsunuz. Olmaz
sanırım. Hocalarımız açıklarsa böyle bir kesme olup olmadığını anlamış oluruz.Saygı ve selamlar.

ete

Kullanmadığım bir yöntem olduğu için tecrübem yok ve biraz data sheet incelemesi gerektiriyor bu sorunun cevabı.
İnceledim ve şunları gördüm. Bir defa ADC işlemini sleep modunda da yaptırmak mümkünmüş . Ancak bu konumda ADC CLK sistemi ADC nin dahili (internal) RC osilatöründen beslenmesi gerekiyormuş.  Bunu sağlamak için define komutlarında;
DEFINE ADC_CLOCK 3 olarak seçilmesi veya ADCON0 registerinin 7-6 nolu bitlerine %11 değerinin verilmesi gerekir. (programda bu şekilde yapılmış).
Bir ADC işlemi tamamlandıktan sonra ADC ölçüm sonucu ADRESH ve ADRESL registerlerine yerleştirilmektedir.
Bu aşamada  GO/DONE biti sıfırlanmakta ve ADC kesme bayrağı biti ADIF=1 yapılmaktadır. Bu işlem kesme oluşmasını sağlamaktadır . Kesmenin oluşması sistemin SLEEP den uyanmasına sebep olacaktır. Tabiiki her şey düzgünce ayarlanmış ise.
Peki sistemin düzgünce ayarlanması için neler yapılmalıdır. Sırası ile aşağıdakilerin yapılması gerekiyor.

1. ADC clock kaynağını RC olarak seç
2. Ansel registerine gerekli ayarlamayı yap
3. ADCON1 registerine gerekli değeri ver.
4. ADIF bitini sıfırla
5. Set ADIE bit
6. SET PEIE bit
7.Set GIE bit
8. ADCIN,xx komutu ile ADC işlemini başlat
9. Sistemi Uyku moduna sok.
10. İŞlem tamamlanınca sistem uyku modundan çıkacaktır.

Şimdi gelelim senin programa;
BASLA:
LCDOUT $FE,1,"LCDFAAL"
LCDOUT $FE,$C0,#MSJ," ",#MSJJ
@SLEEP
satırlarına bakarsan sen LCD komutlarını verip programı uykuya sokmuşsun. Bir ADC işlemi henüz başlamamış peki kim başlatacak program uykuda iken?. Hiç kimse. Kesme ADC yi başlatmak için değil bittiğinde sonucu almak için oluşuyor.
O halde bir şekilde ADC işlemini başlatman gerekiyor. Bunu yapmanın iki yolu var.;
Birincisi ADCIN komutunu kullanıp hemen ardından sistemi uykuya sokma olabilir. Ancak bu durumda BAsic sonucu bekleyecektir büyük ihtimalle. O halde belkide en doğrusu ikinci yöntemi kullanmak olur.
Bunun için;
- ADCON0 registerini kullanarak ADC kanalını seçmek
- Yine ADCON0 regsiterini kullanarak 2. ci biti set ederek ADC işlemini başlatır ve
- programı sleep moduna sokarsın.
ARtık kesme etiketinde ADCIN.0 komutunu kullanmaya gerek yok zaten anlamsız oluyor onu orada kullanmak .
Ancak aynı anda tek kanal okuması yapılabileceği için ikinci kanalın okumasıda kesme etiketinde yapılabilir.
Bunun haricinde söyleyecek başka bir şey yok sanırım.

Ete




mg1980

Ete Hocam,
Konuyla ilgili anlattıklarınız benim için epey ileri seviye bilgiler.Şimdilik teorik olarak okuyup anlamaya çalışıyorum.Bu konuyla ilgili basitçe düşündüğüm şu husus olabilir mi?
1-Arkadaşın yaptığı devrede RA0'a aynı anda bir de optocoupler ekleyip RB0'da değişikliği göstersek bu şekilde kesme oluşturup sonuç alınamaz mı ?
2- Bu sorum da bu konu dışında, bazı programlarınızda değişken tanımlarken HAM VAR WORD 56
şeklinde yazdığınız 56 rakamının anlamı nedir ? Parantez içinde dizi değişkeni olduğunu biliyoruz ama parantezsiz anlamı nedir ? Cevabınız için teşekkürler.Saygılar.

ete

#4
Mevcut programında ki kesme bölümune ilave bir RB0 kesmesi oluşturabilirsen o zaman senin sistem çalışır.

Değişkenlerin yanındaki rakam o değişken için yanındaki adresin kullanılmasını sağlar. Böylece program simulasyonda çalışırken  değişken değerlerini watch window ile izleyebilirsin.
Ete

greatgonzo

Öyle zannediyorum ki ADC kesmesini yanlış yorumluyorum. Zira ETE hocam sizin yönteminizide denedim hatta programın yavaşlaması adına lcd ye değer gönderme kısmını pause 1000 ile yavaşlattım.  Kesme bayrağı sıfırlama ve  kesmeleri aç  nokatalarında  farklı noktalardan komut yolladım ama sonuç nafile. PAUSE 1000 kısmına kadar problemsiz ki ondan sonra sapıtıyor :-) Mantıken ADC okutuyorum lcd ye yollayıp kesme bayrağını sıfırlayıp kesmeleri açıyorum. Kesme kısmında da kesmeleri kapatıp bayrağı sıfırlıyorum. Ardından ADC kısmına sıçrıyorum. Ayrıca okuma tek kanaldan yapılıyor. Kesme olmadığında sorun yok 2 kanalda değer okuma normal ama kesme konduğunda rb bacaklarını söksem bile ( hiç bir değer okumaması hatta kesme oluşmaması bile gerekirken) yine aynı seklde owerflow taşması yaratıyor.

Bu bağlamda işte ADC kesmesini yanlış anladığımı düşünüyorum. Datasheet tede bahsedilen ADC kesmesi sleep modundan uyandırabiliyor ama bu işlem okuma gerçekleştikten ve ilgili yazmaçlar değer aldıktan sonra gerçekleşiyor.  Benim asıl amacım ADC değeri değiştiği değiştiğinde pic uyanması ve o değere göre işlem yapmasıydı. Sonrasında portun rutin  bekleme değerinde uyku moduna geçmesi ve sonraki değişikliği beklemesi idi. Görünen o ki mümkün değil. Zira ADC portuna giriş olduğu sürece kesme vektörüne gibiyor hatta sürekli orada kalıyor gibi. Ayrıca uyku modunda bu kıyaslamayı nasıl yapacak. Yapsada faydası ne olur. Zira dahili osilatör ve ADC çalışıp enerji çekecek. Sleep moduda olsa hatrı sayılır biçimde pic enerji tüketecek.

Sanırım bu özellik hassas bir ADC okuması için dizayn edilmiş. Yani okuma noktasında hiç bir işlem olmaması amaçlanmış. Yada  proteus beni yanıltıyor ve gerçekte okuma yapılan kanal 0 noktasında duruyor ve değer ürettiğinde bacağa ilgili gerilimi veriyor. Bu durumda sıhatli bir uyanma ve okuma gerçekleşiyor ki buda tartışılır. Genelde okunacak olan sensör vs hep bir çıkış verecektir.

Benimde aklım rb0 yada rb4-7 bacaklarındaki değişikliğe gitti ama onlarda analog değer okuyamayacağından sadece low high değerlerine göre kesme üretecek. Bu seçenek elenince geriye harici bir donanım kullanmak kalıyor. Yani adc değerimde değişiklik olunca sadece ilgili porta değer yollayacak ve normal şartlarda bacağı 0 da tutacak gibi.

Birde komparatör pinleri düşündürüyor. Zira 16F88 de komparatörlerde kesme oluşturabiliyor. Ama onla ilgili bilgim yok sadece bacakları sıralamasına göre kesme oluşturuyor diye biliyorum.

Gene bana datasheet yolu göründü :-) 1-2 gundur ADC kesmesi için kıt ingilizcemle karıştırıyordum birde comparatör için deneme yapacağım. Olumlu olumsuz sonuç bildiririm. Yine yukarıda mantık hatam varsa lütfen uyarın:-) Değerli ETE hocam ve mg1980 arkadaşım yanıtlarınız için teşekkür ederim.


greatgonzo

Araştırmam devam ediyor ve tam yılan hikayesi oldu:-) Göründüğü kadarıyla CCP nin "compare"  kısmıyla mümkün. Compare  başlığında "special event trigger" diye bahsedilen bir olgu. CCP1 pinlerinden birinin ayarlanması gerekiyor. Timer1 sayacı açılması gerekiyor. İlgili compare işlemi bu sayaç ile yapılıyılıyor. 16 bitlik değer  8+8 olarak kaydediliyor. Değerin değişmesi durumunda timer1 resetleniyor. Ayrıca ccp modülüde kesme üretiyor. Special event trigger bu noktada devreye giriyor. Eğer ADC ayarları yapıldıysa Timer1 sıfırlandığında ADC modülünü tetikliyor. Oda ADC tamamlanınca kesme üretiyor. Ama hiç bir kesmeden faydalanamadım. Donanımsal olarakta yanlış yapıyor olabilirim. ( ccp1 pini ile adc pinini köprülemiştim, belkide, yanlışta olsa bir yaşam belirtisi olur diye  ) Sleep fonksiyonu bir yana  herhangi bir kesme ile ADC yaptığımda mutlaka sonu overflow olarak bitiyor. Nettede sıhatli birşey bulamadım.Bir süre kendi haline bırakacağım. Gelişme olursa not düşerim tekrar buradan.

İyi çalışmalar...

Powered by EzPortal