fonksiyonlar ve programlama mantığı sorunu

Başlatan mcan, 06 Ocak 2006, 15:28:02

mcan

#include <16F84a.h>
#fuses XT
     .
     .
     .
int a=0;
int16 b=0;...


aaa(){
...............
xyz();}


xyz(){
..........
enable_interrupts(GLOBAL);
........
disable_interrupts(GLOBAL);
aaa();}


abc()
{disable_interrupts(GLOBAL);
xyz();}



#INT_TIMER0
arttir()
{......
 abc();}






void main()
{
       enable_interrupts(INT_TIMER0);
       enable_interrupts(GLOBAL);
       lcd_init();
       abc();
       xyz();

}


fonksiyonların birbirini çağırmasını istiyorum bu mümkünmü?
bunları sonsuz döngü içine sokarak halladebildim istemeyerekde olsa,birde şu sorunum var kesmeyi aktiflediğim anda timer 0 taşmadığı halde kesmeye gidiyor bunun nedeni nedir

mcan

birde şu sorunum var,bir kesme alt programı içinden örnekdeki gibi başka bir fonksiyon çağırırsak,çağırılan program çalışırken sanki kesme bitmemiş gibimi çalışır?kesme gelince bi takım işlemler ve sonunda bir yere dallanacak dallandığı yerde artık kesmeyle alakası kalmamış olacak bunun için dallandığı yere hemen disable_interrupt global vede hangi kesmeyse onu pasif yapan dizeyi yazıyorum, bu doğru bir çözüm mü?

Petek

Alıntı yapılan: "encryptedcode"fonksiyonların birbirini çağırmasını istiyorum bu mümkünmü? bunları sonsuz döngü içine sokarak halladebildim istemeyerekde olsa,
Recursive calling CCS tarafından desteklenmiyor. C18 18F için destekliyor ama o da sonsuz sayıda değil
Alıntı Yapbirde şu sorunum var kesmeyi aktiflediğim anda timer 0 taşmadığı halde kesmeye gidiyor bunun nedeni nedir
Timer 0 interrupt flagini resetle, timer0 i sıfırla ve interruptını öyle aktifle.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

mcan

şimdilik beceremedim o işi şunu ekledim fayda etmedi
#ASM bcf 0X0B,0B00000000 #ENDASM

Petek

Alıntı yapılan: "encryptedcode"birde şu sorunum var,bir kesme alt programı içinden örnekdeki gibi başka bir fonksiyon çağırırsak,çağırılan program çalışırken sanki kesme bitmemiş gibimi çalışır?
Evet. Kesme ancak kesme rutininin son komutu da işlendikten sonra biter (yani retie komutu ile kesme olmadan önceki yere geri döner)
Alıntı Yapkesme gelince bi takım işlemler ve sonunda bir yere dallanacak dallandığı yerde artık kesmeyle alakası kalmamış olacak
İnterrupt işlemi gerçekleştiğinde programın icraası 0x04 adresine gider. Orada işlemler yapılır ve retie ile geri döner. Dallandığı yerin kesmeyle alakası kalmayacak ne demek?

Alıntı Yap
bunun için dallandığı yere hemen disable_interrupt global vede hangi kesmeyse onu pasif yapan dizeyi yazıyorum, bu doğru bir çözüm mü?
Hocam kesme geldimi zaten gie biti 0 olur. Başka interrupt almaz. 0x04 adresinden sonra yapılan işlemlerle  işini bitirip retie komutu ile geri döndüğünde gie biti otomatik set edilir.

Sana bir tavsiye. Mümkün olduğunca interrupt rutininde başka bir alt program çağırmamaya çalış. Mümkünse bit yada bayt tanımlı değişkenleri set reset et ve kesmeden geri dön. Döndüğünde bu flagleri kontrol edecek if blokların yada alt programların olsun.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

mcan

Alıntı YapEvet. Kesme ancak kesme rutininin son komutu da işlendikten sonra biter,(yani retie komutu ile kesme olmadan önceki yere geri döner)
yani ben kesme içinde bir alt programn çağırırsam,kesme onuda işler hatta içinde sonsuz döngü varsa orada takılabilir,tüm bunları bitirirse birde sonuna retie ekleyerek nereden geldiyse oraya geri döner... aslında duruma göre çok işe yarayabilir ama keşke kesme rutininin sonunda ki retie yi iptal edebilsem:bu retie komutu c deki return komutuyla aynı gibimi?anladığım kadarıyla retie yi derleyici bizim istediğimiz yere değil kesmenin en son işlenen komutunun sonuna koyuyor.bunu koymaması için bişey yapılabilirmi?

Alıntı Yapİnterrupt işlemi gerçekleştiğinde programın icraası 0x04 adresine gider. Orada işlemler yapılır ve retie ile geri döner. Dallandığı yerin kesmeyle alakası kalmayacak ne demek?
olayı şimdi daha iyi anladım alakası kalmayacak şu oluyor,retie nasıl iptal edilir :D demek istediğim buymuş

Alıntı Yapbunun için dallandığı yere hemen disable_interrupt global vede hangi kesmeyse onu pasif yapan dizeyi yazıyorum, bu doğru bir çözüm mü?
bilmeden de olsa burada şunu ummuşdum : kesmeleri pasif edince ne retie kalır ne bayrak biti kesmeyi hiç kuramamışım gibi sıfırlanır,demek pasif etsekde bir önceki kesmenin komutları işlenene kadar kesme geçerli

olayıda anlatayım,şimdi önce önbölücü alt programı çağırılıyor timer0 ı kuruyorum ve belli bir süre sonunda değerini okuyorum bu arada timer0 taşarsa kesme  oluyor oradan bir alt program olan önbölücü kısmına gidiyor(aslında gidiyor ama hala kesme için de oluyor ki ben bunu istemiyordum)tekrar ayarlanıp timer0 yeni önbölücü değeriyle tekrar kuruluyor ,kesme olmassada sürekli aynı önbölücü değeriyle çalışıyor.....

şimdilik bunu tüm programı sonsuz döngü içine alıp kesmeyide sadece bir değişkeni değiştirmesi için kurdum sorun yok ama her ölçümden önce tekrar tekrar aynı komutlar işleniyor benim istediğim önbölücü programına geldiğinde geri falan dönmesin yeni bir kesme olayını ilkinki gibi beklesin.

şimdi birde şu yolla anlatayım
program başlıyor: işlem;işlem;---- a()----->b()<------->c()
                                ^                   l          
                                l____kesme__________l

Petek

Alıntı yapılan: "encryptedcode"yani ben kesme içinde bir alt programn çağırırsam,kesme onuda işler hatta içinde sonsuz döngü varsa orada takılabilir,tüm bunları bitirirse birde sonuna retie ekleyerek nereden geldiyse oraya geri döner...
Evet
Alıntı Yapaslında duruma göre çok işe yarayabilir ama keşke kesme rutininin sonunda ki retie yi iptal edebilsem
Edersin. Hiç problem değil. Ne işine yarayacak? Yani ana programa döndüğünde kesmeyi kullanacaksan bu durumda gie bitini tekrar set edeceksin. retie ile bu iki işlemi tek işlemle hallediyorsun
Alıntı Yap:bu retie komutu c deki return komutuyla aynı gibimi?anladığım kadarıyla retie yi derleyici bizim istediğimiz yere değil kesmenin en son işlenen komutunun sonuna koyuyor.bunu koymaması için bişey yapılabilirmi?
retie ile return aynı işi yapıyor. retie fazladan gie bitini set ediyor. Evet yapılabilir. ISR rutinini assembler ile yazabilirsin. Derleyiciye ISR rutinini tanıtmak zorunda değilsin. Sadece assemblerda yazdığın rutin 0x004 adresinden başlasın yeterli.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

mcan

şimdi petek hocam,tam kavrayamadım nasıl yapacağım :oops: programımda  hic
#int_timer0
kullanmadan
programın başınamı neresine, şöyle bişeymi yazayım
KESME(){
#asm 
org 004h 
bcf 0x0B,1
bcf 0x0B,2
BSF 0X0B,7
#ENDASM
A();}


Bir de TO1F ile INTF nin farkını kavrayamadım elimdeki kitapda aynen şöyle yazıyor

TO1F:
1-tmr0 taştı
0-taşmadı

INTF
1-tmr0 dış kesme oldu
0-dış kesme yok

to1f den hadi şöyle bişey anladım diyelim:004 e sapınca bunu kotrol edip kesme timer 0 danmı kaynaklı ona bakabiliriz ...ama ıntf için aklıma bişey gelmiyor

Petek

İlk önce 16F serisi bir uc nin datasheetine bir gözat. Orada kesme rutinine girdiğinde saklaman gereken registerleri nasıl saklayacağını anlatıyor. İlk önce wreg içeriğini daha sonra status registerini içeriğini bozmadan (swap komutu ile) wrege, aynı şekilde wreg den f'e aktaracaksın. Aşağıda 628 datasheetinin  ilgili sayfasından alıntı yaptım.

Alıntı Yap
14.7 Context Saving During Interrupts

During an interrupt, only the return PC value is saved on the stack. Typically, users may wish to save key registers during an interrupt e.g. W register and STATUS register. This will have to be implemented in software.

Example 14-1 stores and restores the STATUS and W registers. The user register, W_TEMP, must be defined in both banks and must be defined at the same offset from the bank base address (i.e., W_TEMP is defined at 0x20 in Bank 0 and it must also be defined at 0xA0 in Bank 1). The user register, STATUS_TEMP, must be defined in Bank 0. The Example 14-1:

• Stores the W register
• Stores the STATUS register in Bank 0
• Executes the ISR code
• Restores the STATUS (and bank select bit register)
• Restores the W register

EXAMPLE 14-1: SAVING THE STATUS AND W REGISTERS IN RAM
MOVWF W_TEMP ;copy W to temp register,;could be in either bank
SWAPF STATUS,W ;swap status to be saved into W
BCF STATUS,RP0 ;change to bank 0 regardless ;of current bank
MOVWF STATUS_TEMP ;save status to bank 0 ;register
:
: (ISR)
:
SWAPF STATUS_TEMP,W ;swap STATUS_TEMP register into W, sets bank to original state
MOVWF STATUS ;move W into STATUS register
SWAPF W_TEMP,F ;swap W_TEMP
SWAPF W_TEMP,W ;swap W_TEMP into W


Org komutu assembler içerisinde kullanmayacaksın. CCS nin org komutu var help ten bak (#ORG).

Yalnız interrupt yada kesme rutinine girdikten sonra bu rutinden normal return ile çıkarsan saklamış olduğun wreg, status reg eski değerlerini geri almayacağından ana programda (kesmeden önceki değerler) başka değerlere dönmüş olacak. Bu da programında istemediğin sonuçlara götürür.

İnterupttan ne zaman çıkmak istersen return yerine "context saving" örneğinde görünen ikinci kısmın başına (ISR) den sonra "SWAPF STATUS_TEMP,W " ile başlayan yere goto ile git.  

Sana tavsiyem, interruptta mümkünse alt program çağırma. Eğer interruptsız 5 stack alanı kullanıyorsan interrupt kullandığın anda bu 5 + 1 olur. İnteruptta bir alt program çağırmış isen bu durumda 5 + 2 olur. LCD yazdırma işlemi yaptırıyorsan CCS de 4 stack sadece LCD tüketir (biraz oynarsan 3 e indirirsin). Yani stack alanını ekonomik kullanman gerekir. Olmuyor ise PIC18 serisinde 32 adet stack alanı var. Tepe tepe kullanırsın.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

mcan

hocam gecenin bi yarısı oldu ,sana çok teşekkür ederim...şimdi son kafamda şekillenen şu, yani programıma şunu ekleyeceğim
#org 0x004 
kesme() {

#asm
movwf w_gecici
swapf status,w
clrf status
movwf status_gecici
movf pclatch,w
movwf pclacth_gecici
clrf pclacth_gecici

bcf 0x0B,1 
bcf 0x0B,2 
BSF 0X0B,7
#endasm

işlem;
işlem;
a=1;
alt_program(); :))) 
}

altprogram(){
if(a==1){
#asm
movf  pclacth_gecici,w
swapf status_gecici,w
movf status
swapf status_gecici,f
swapf status_gecici,w
#endasm
a=0;}

işlem;
işlem;
.
.
.
stackda 8 taneymiş :lol:  az kullanmak gerekiyor ama o kadar profosyonel olamadım şimdilik öğrenmek adına yazdığım şey olurmu hocam

Petek

Kesme rutinini yazdın ve içerisinden alt_program() çağırdın. Altprogram işini bitirdikten sonra kesme rutinini normal bitiriyorsun. Burada derleyici senin interrupt içerisinde olduğunu bilmediği için normal return ile ana programa döndürür (ana programında interruptı tekrar aktiflemen gerekir). Fakat sakladığın status, wreg içeriği tekrar yüklenmediği için ana programda istemediğin durumlar olacaktır. Bu olay aynı televizyonlarda yayımlanan sırlar dünyası, kalp gözü, vs programlarının senaryolarına benzer. Adam hayatının o anına kadar hep kötülük yapar, ta ki o rüyayı görene kadar. Ertesi gün hidayete ermiş başka biri olarak hayatına devam eder. Neden? Çünkü wreg ve statüs registerleri geri yüklenmedi  8)

Alt programında bu işi yapmaya çalışmışsın ama en sonuna koyduğun a=0 ilk önce wreg i daha sonra da status registerini değiştirecektir. Bu durumda assembler içerisinde yazdıklarının anlamı kalmayacak ve gerksiz delay işlemleri olarak algılanacaktır (tarafımdan :))

Alt programı bitirmemişsin. Bu durumda alt programı ana program gibi kullanmak niyetindeysen olmaz. Stackta bir alan tükettin ve stack pointerin 1 arttı.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

mcan

#org 0x004 
kesme() { 

#asm 
movwf w_gecici 
swapf status,w 
clrf status 
movwf status_gecici 
movf pclatch,w 
movwf pclacth_gecici 
clrf pclacth_gecici 

bcf 0x0B,1 
bcf 0x0B,2 
BSF 0X0B,7                  
#endasm 

işlem; 
işlem; 
a=1; 
alt_program(); :)))  >---------------
}                                                l
                                                  l_ buraya dallanır(?)
altprogram(){  <-------------------  l
if(a==1){ a=0;
#asm 
movf  pclacth_gecici,w 
swapf status_gecici,w 
movf status 
swapf status_gecici,f 
swapf status_gecici,w --------------> bu  işlemle beraber return   
#endasm                                       gibi etki mi oluşur                           
} ----->buradan ,,içeriği değiştirilmiş değerlerle de  
olsa,,normal return ile ana programa nasıl oluyorda döndürebiliyor, düz mantık olarak ne retfie ne return nede #int_xxx kullandım,hangi mekanizma onu ana programa döndürüyor?

eğer buradan anaprograma dönerse a=0; gibi bir işlem yaptığımdan ana programa gelmeden hemen önce içerikleri değiştirmiş ve o satırları boşa yazmış oluyorum değilmi? ozaman a=0 işlemini başa alıyım

işlem; 
işlem; 
. 
. 
.
ayrıca stack olayınıda okudum recursive calling gibi olacak buda ,recursive calling de fonksiyonlar birbirini çağırdıkça hep yarım kalmış fonksiyonlar zinciri oluşup stack alanının dolmasına vede programın belli birdüre sonra çakılmasına yol açıyormuş  :D bende kesme içinde alt program çağırdığım zaman sanırım aynı şey olacak ve her kesme oluşunda stack bir artıp sonunda tükenecek doğrumu acaba? vede satack ne yaızlabilir nede okuna bilirmiş böylece prpogram koduyla geri alamam sanırım pop ve push işlemleride yapılamıyormuş haydi hayırlısı ben en iyisi dediğini dinleyip fazla diretmeyeyim diyorum ama, merak işte şunuda bitirip denemek olacakları gözümle görmek istiyorum

asıl üzerinde çalıştığım kodu ekliyorum onun üzerinden konuşalım vaktiniz varsa

mcan

#include <16F84a.h>
#fuses XT,NOWDT,NOPROTECT,NOPUT,//NOLVP,NOBROWNOUT
#use delay(clock = 4000000)
#define use_portb_lcd
#include <lcd.c>
#byte  PORTA = 5

int a=0;
int16 b=0;
int c=1;


abc()
      {
      disable_interrupts(INT_TIMER0);
      disable_interrupts(GLOBAL);


   switch(a)
   {case 0: setup_timer_0 (RTCC_DIV_1|RTCC_EXT_L_TO_H);break;
    case 1: setup_timer_0 (RTCC_DIV_2|RTCC_EXT_L_TO_H);c=2;break;
    case 2: setup_timer_0 (RTCC_DIV_4|RTCC_EXT_L_TO_H);c=4;break;
    case 3: setup_timer_0 (RTCC_DIV_4|RTCC_EXT_L_TO_H);c=4;break;
    case 4: setup_timer_0 (RTCC_DIV_8|RTCC_EXT_L_TO_H);c=8;break;
    case 5: setup_timer_0 (RTCC_DIV_16|RTCC_EXT_L_TO_H);c=16;break;
    case 6: setup_timer_0 (RTCC_DIV_32|RTCC_EXT_L_TO_H);c=32;break;
    case 7: setup_timer_0 (RTCC_DIV_64|RTCC_EXT_L_TO_H);c=64;break;
    case 8: setup_timer_0 (RTCC_DIV_128|RTCC_EXT_L_TO_H);c=128;break;
    case 9: setup_timer_0 (RTCC_DIV_256|RTCC_EXT_L_TO_H);c=256;break;
    default :lcd_putc("\fPs ");a=0;break;
   }

      set_timer0(0);
      enable_interrupts(INT_TIMER0);
      set_timer0(0);
      enable_interrupts(GLOBAL);
      FOR(;;)
               {
                  set_timer0(0);
                  delay_ms(1000);
                  b=get_timer0();
                  set_timer0(0);
                  disable_interrupts(INT_TIMER0);
                  disable_interrupts(GLOBAL);
                  b=b*c;
                  lcd_gotoxy(1,1);
                  printf(lcd_putc,"deger %6lUHz   ",b);  }
}   








#INT_TIMER0
arttir()
{
a++;
printf(lcd_putc,"\n %U   ",a);}



void main()
{
       enable_interrupts(INT_TIMER0);
       enable_interrupts(GLOBAL);
       lcd_init();
       abc();

}
işte kesmeyle birlikde abc programına gitmesini istiyorum yoksa tüm abc programını sonsuz döngüye aldırıp anca öyle çalışmasını sağlıyorum, böylece her değer ölçmeden tekrar tekrar ayar yapılıyor.

kesme geldiğinde sayaç yeni önbölücü değeriyle bir kere kurulup sonsuz döngüde çalışmaya koyulacak bunu nasıl yaparım?

biraz önce ccs de sadece kesme ve alt program denedim böyle yapınca kesme oluşmuyorki :P  aynen şöyle bişey yazdım

#include <16F84a.h>
#fuses XT,NOWDT,NOPROTECT,NOPUT,//NOLVP,NOBROWNOUT
#use delay(clock = 4000000)
#define use_portb_lcd
#include <lcd.c>
#byte  PORTA = 5

int a=0;
int16 b=0;
int c=1;


abc(){b=0;


b=b+a;





                  FOR(;;) {
                  b=b+1;
                  delay_ms(250);

                  lcd_gotoxy(1,1);
                  printf(lcd_putc,"deger %6lUHz   ",b);  }}



ab(){lcd_putc("\fKESME");delay_ms(2500);abC();}



#INT_EXT
kess(){;
a=a+10;
ab();}



void main()
{

       lcd_init();
      ext_int_edge(L_TO_H);
       enable_interrupts(INT_EXT);
       enable_interrupts(GLOBAL);
       abc();

}


işte bunda abc yi kesme içinde herhangi bir yerde çağırırsam rb0 dan sinyal gelse bile kesmeye gitmiyor ilginç,yapmaya çalıştığım şey ccs de zaten mümkün değilmiş siz ne dersiniz gene bende bi hata olabilir

Petek

Alıntı yapılan: "encryptedcode"ayrıca stack olayınıda okudum recursive calling gibi olacak buda, recursive calling de fonksiyonlar birbirini çağırdıkça hep yarım kalmış fonksiyonlar zinciri oluşup stack alanının dolmasına vede programın belli birdüre sonra çakılmasına yol açıyormuş  :D bende kesme içinde alt program çağırdığım zaman sanırım aynı şey olacak ve her kesme oluşunda stack bir artıp sonunda tükenecek doğrumu acaba? vede satack ne yaızlabilir nede okuna bilirmiş böylece prpogram koduyla geri alamam sanırım pop ve push işlemleride yapılamıyormuş haydi hayırlısı ben en iyisi dediğini dinleyip fazla diretmeyeyim diyorum ama, merak işte şunuda bitirip denemek olacakları gözümle görmek istiyorum
Yazdıklarınız doğru. Bir de kesme alt programında çağırdığınız alt programı kesmeye almanız daha doğru olur. O alt programdaki assemblerda da hata var zaten :) pclath wreg e alındı hemen arkasından wrege başka bir tane daha..

Denemeyi ISIS üzerinde yapın. Simulasyonu kusursuz diyebilirim. pinlerdeki read-modify-write (rmw) özelliğini bile eklemişler. İnanılmaz birşey... test ettiğimde çok şaşırmıştım... Yani ISISa güvenebilirsin.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein

Petek

İkinci mesajın çok dağınık olmuş, anlamakta güçlük çekiyorum. İlk kod bölümü ile ilgili yazayım. Ana programda abc bir defa çağırılıyor ve abc de iş bittikten sonra bir daha abc çağrılmaz, program ana programın sonunda sonsuz döngüye girer ve bekler. Bu arada interruptının a değerini arttırması bir anlam ifade etmez. Çünkü artık programın sonsuz döngüde (daha doğrusu dos altında olsaydı program normal işleyişini tamamlayıp Dos a dönmüş olurdu)..

Daha önce de belirttiğim gibi kesme rutinlerinde alt program çağırma işlemini  mümkün ise yapma. Hele interrupt rutinlerinde LCD ye bir şey yazdırmak (normalde 4 stack tüketir, bir de interruptın kendisi 5, yani ana programda sana 3 satcklık bir yer kalmış !!!) son derece sakıncalı. Bunu anlaman için şöyle bir örnek vereyim. Ana programda LCD ekrana bir şey yazdırıyorsun. LCD rutinleri iç içe 4 alt program çağırır. Senin programın icraası tam bu 4. de iken interrupt geldiğinde 5 stack alanı daha kullanılır, toplam 9 eder. 9. stackın ilk baş kısmına yerleşir. İnterrupttan döndüğünde LCD işlemin de bittiğinde ana programa dönmek yerine yine LCD rutinin en derindeki alt programına gidersin ve hiç bir zaman ana programa dönemezsin.
“Önyargıları yıkmak atomu parçalamaktan zordur” A.Einstein