Nisam dovoljno ispratio temu, zbog nedostatka vremena, Milane.
Odavno nisam pisao u Pic Basic i odavno sam ga napustio zbog dosta njegovih mana, kako Želja reče upravo zbog mana sa ISR.
Ne znam koji Pic trenutno upotrebljavaš, ali svi koji imaju hardverski PWM modul (CCP) se koriste tako što se konfiguriše taj modul kao PWM, potom se samo u jednoj ili najviše nekoliko instrukcija (zavisno da li radiš pwm sa 8 ili više bita) upisuje vrednost u registre pwm modula.
Upisivanje je na nivou asm, bez obzira u kom jeziku pišeš. Na primer, ako je osmobitna vrednost nekog pwm za modul CCPR1, onda upisuješ u njegov donji bajt tu vrednost.
Ovako to izgleda u Pic Basic, ako se dobro sećam sintakse (nisam ga koristio bar 12 godina):
Code:
CCPR1L = neka_vrednost
U C to izgleda skoro isto:
Code:
CCPR1L = neka_vrednost;
Ukoliko je to više od 8 bita, obično postoji registar u kome u neke bitove upisuješ ono što je preko 8 bita, na primer ovako kod 18F serije i u C jeziku (; je terminacija jedne linije a // je komentar u jednoj liniji u C):
Code:
// pwm1_val je 16bit varijabla
// prvo se smeštaju viši bitovi na potrebne lokacije, obično su to bitovi u registru CCPxCON
DC1B0=(bit)pwm1_val;
DC1B1=(bit)(pwm1_val>>1);
CCPR1L=(pwm1_val>>2); // potom se niži bajt vrednosti smesti u niži bajt CCP modula.
(U Pic Basic je to isto osim što se ne koristi ; za terminaciju linije, koliko se sećam...)
Konfiguracija PWM modula je takođe istovetna u oba jezika (pošto je asm) i najpametnije je da je direktno uradiš a ne preko nekih biblioteka koje su ti "nevidljive".
Na primer, za 18F seriju na 40MHz (i većinu 16F) i na primer CCP1 modul, konfiguracija kao PWM za na primer od 0-100% i na primer frekvenciju 6,25KHz (/* je takođe komentar u C, samo važi od /* do */ bez obzira na broj linija):
Code:
// pwm 1 i/ili 2 , za 8bita(0-100%) 6.25KHz
PR2 = 99;
T2CON = 0x07;
CCP1CON = 0b00001100;
// CCP2CON = 0b00001100;
/* iskomentarisan kod kao alternativna upotreba drugog PWM sa istim performansama. dovoljno je odkomentarisati // CCP2CON i odmah može da radi i drugi pwm */
Kada je pwm osmobitni, kao na primer ovaj poslednji konfig od 0-100%, upisivanje traje jednu instrukciju koja je na primer kod PIC16F1783 na 32MHz kloka 125nS svega.
To vrlo beznačajno opterećuje interrupt pa naravno možeš vršiti modifikaciju pwm u okviru interrupta.
PWM će kod Pic MCU prihvatiti novu vrednost trenutno po isteku prethodnog pwm perioda (u ovom slučaju 1/6.25KHz).
Ukoliko taktuješ te događaje na primer nekim tajmerom, poput TMR0, i ako želiš rezoluciju sinusa od na primer 100 vrednosti, onda konfigurišeš TMR0 da ti izaziva prekid na 100uS i brojiš svaki put do 100, tako da ti se sve vrti na 10mS tačno, da bi imao jedan polutalas događaja.
Mora se iskontrolisati i izvor takta za TMR0 kao interni i preskaler da bi dobio željeno vreme.
Code:
RCON = neka vrednost; /* Compatibility mode. Priority. interapti */
T0CON = neka vrednost; /* prescaler, clk source... */
INTCON = neka vrednost; /* GIE,TMR0IE... */
Naravno napraviš i lookup tabelu sa 100 vrednosti za sinus (jedan polutalas).
Ovako bi na primer to osvežavanje pwm izgledalo u C (a ti prevedi u Basic jer je veoma slično, da se sad ne bavim time):
Code:
void interrupt int_hand (void) //funkcija koja se bavi interruptom, kod Basic je to labela koja se malo drugacije zove
// posto si je vec koristio ne bih se bavio prekopavanjem oko imena
{ //pocetak funkcije
if ( TMR0IF && TMR0IE ){ //uslov, ako je flag od TMR0 i (and) je odobren isr TMR0 onda..
TMR0H=TimerH; //upisi vrednost viseg bajta u TMR0, za njegovu sledecu iteraciju
TMR0L=TimerL; //upisi vrednost nizeg bajta u TMR0, za njegovu sledecu iteraciju
//vrednosti natrimovati na tacno 100uS, preskalerom i vrednostima oba TMR0 registra
test_pin ^=1; // toglovati neki spoljni pin da bi skopom mogao da meris vreme
// to je kod tebe naredba toggle, koliko se secam
// jedno stanje traje koliko i isr, H=100uS i L=100uS, odnosno isr je 100uS
// ujedno stanje ovog pina (ili npr nekog flag) koristiš za promenu polariteta izlaza
//na primer, ako je test_pin H onda je pozitivan polutalas, ako je L onda negativan
if(test_pin) h_bridge=positive; // prebacivanje polariteta H mosta, na osnovu stanja koje se togluje u isr
else h_bridge=negative;
int_timer++; // u svakom prolazu uvecaj ovu varijablu za 1, trebaće ti za neke tajmere u main
int_temp++; // u svakom prolazu uvecaj ovu varijablu za 1, ona pravi period od 10mS
if(int_temp >=100) int_temp=0; //resetuj je svaki put kad dostigne 100, tj. na svakih 10mS
CCPR1L=lockup_table[int_temp]; //upisi u pwm vrednost iz tabele, indeksiranu sa int_temp
//vrednost se osvežava na svakih 100uS
} // isto što je kod tebe endif
TMR0IF = 0; // ocisti TMR0 flag da bi omogucio novi interrupt
} // kraj interrupt funkcije ili kod tebe labele
Ti sa 100uS u jednoj iteraciji tajmera TMR0, imaš na raspolaganju prostora za SKORO 800 instrukcija u okviru samog događaja, sa na primer PIC16F1783 na 32MHz !
Nije isr toliko tesan koliko se misli, ako u njemu ništa drugo ne obavljaš. Resurse mu troše komunikacije na primer, poput RS232, I2C, SPI i slično, i naravno blokiraju ga loše napisane delay funkcije, koje ako su loše napisane okupiraju potpuno MCU za vreme svog trajanja.
Za to je na primer pametnije koristiti jednu varijablu koja se stalno uvećava u isr (nazvao sam je int_timer u prethodnom kodu) i neko merenje vremena bi izgledalo ovako:
Code:
neka petlja u main programu
if (neki_dogadjaj) int_timer=0; //resetuj varijablu, kao start tvog timera
if (int_timer>= neko_vreme) uradi_nesto; //kada istekne neko_vreme x 100uS,uradi nesto sto treba
// radi nesto sto vec radis u petlji glavnog programa, potpuno neopterecen merenjem nekih vremena
kraj main petlje
Ovo je način merenja vremena koji ne opterećuje glavni program, a isr ti radi pa radi ono što mora...
Ako ti treba vise tajmera paralelno onda napraviš više varijabli koje se inkrementuju u isr.
Svega desetak instrukcija će ti oduzeti na primer "motanje" long (32 bit varijable) u isr, što ti može obezbediti timer od koju stotinu uS pa do oko skoro 120 časova, i da ti ne pravi frku u glavnom programu....
Na primer word varijabla (16bit) ti oduzima samo dve instrukcije u isr a omogućava merenje od 100uS do 65535 x 100uS, tj. do 6.5 sekundi...
Možeš na primer resetovati int_timer na svakih 10000 i u tom trenutku uvećati neku treću varijablu kao referencu za 1 sekundu, i tako dalje i tako dalje...
Nadam se da sam pomogao...