PIC + LCD stopwatch

Projekti naših članova foruma koji su još uvijek u fazi izrade.

Moderators: pedja089, stojke369, [eDo], trax

Post Reply
Plexer
Posts: 3
Joined: 16-05-2015, 17:07

PIC + LCD stopwatch

Post by Plexer »

Pozdrav
Pokušavam napravit štopericu koja će se pokrenut/zaustaviti na pritisak buttona i vrijeme prikazivat na LCD-u. Dodao sam još jedan button koji je aktivan dok štoperica radi (pokušava radit) i svaki pritisak na taj button se counter povećava za 1 (pokušava se povećavat za 1) i stanje se također prikazuje na LCD-u.

Dakle ispis na LCD bi trebao izgledat ovako nekako:
TIME: 00:00:00 - min:sec:ms
HITS: 00 -broj pritisaka na hits button dok štoperica radi

Uglavnom to je ideja ali normalno meni to baš ne radi tako :( . Što se HITS countera tiće on mi izbroji malo više “hitseva” od jednog ali time se planiram pozabavit kasnije. Najviše me muči što štoperica ne radi ni približno realnom vremenu. Za računanje vremena koristim timer0. Gledao sam u datasheet i mislim da sam dobro poštelao registre za sve što mi treba (interrupt enable, prescaler itd.) Uglavnom ja sam sebi htio poštelat da se interrupt dogodi svakih 1ms i kad uđe u interrupt rutinu poveća brojač za 1. Deset puta tako i trebao bi dobit 1 stotinku. Sve to divno i bajno zvuči u mojoj glavi ali kad pokrenem simulaciju toga u proteusu ne dobijem ni približno realno vrijeme. Kad istu konfiguraciju timera koristim za paljenje i gašenje LED-ice radi odlično ali čim ubacim LCD sve se uspori. Iz toga sam zaključio da ga LCD kolje ali od tuda ni makac. Blejim već 2 dana u jedno te isti kod i stojim na mjestu.

Pls help

Compiler: mikroC
Simulator: Proteus
PIC: pic16f887

Code: Select all


//LCD connections
sbit LCD_RS at RC2_bit;
sbit LCD_EN at RC3_bit;
sbit LCD_D4 at RC4_bit;
sbit LCD_D5 at RC5_bit;
sbit LCD_D6 at RC6_bit;
sbit LCD_D7 at RC7_bit;

sbit LCD_RS_Direction at TRISC2_bit;
sbit LCD_EN_Direction at TRISC3_bit;
sbit LCD_D4_Direction at TRISC4_bit;
sbit LCD_D5_Direction at TRISC5_bit;
sbit LCD_D6_Direction at TRISC6_bit;
sbit LCD_D7_Direction at TRISC7_bit;

// all variables
unsigned t_1ms;
unsigned char fr1, fr2, sec1, sec2, min1, min2;
unsigned char hits1, hits2;

void interrupt() {                      // Interrupt routine
  if (TMR0IF_bit) {                     // TMROIF_bit > interrupt flag bit
    t_1ms++;                            // Increment counter every 1 ms
    TMR0IF_bit = 0;                     // clear TMR0IF_bit > Set flag back to "0"
    TMR0   = 0x8;                       // Set initial value on TMR0 register (0x8 = 00001000);
  }
}

Init_LCD() {
    Lcd_Init();                         // Initialize LCD
    Lcd_Cmd(_LCD_CURSOR_OFF);           // Turn cursor off
    Lcd_cmd(_LCD_TURN_ON);              // Turn LCD ON
    Lcd_out(1,1,"Time:   00:00:00");    // Show start values on LCD
    Lcd_out(2,1,"Hits:         00");
}

Init_timer0() {
    OPTION_REG = 0x82;                  // Assign prescaler to TRM0 1:8 and disable PORTB Pull-up Enable bit
    TMR0  = 0x8;                        // Timer0 initial value (8 > 00001000) 256-250+2
    INTCON = 0xA0;                      // Enable TMRO interrupt
    t_1ms = 0;                          // Set counter to zero
}

start() {
     do {
       if (Button(&PORTD, 1, 1, 1)) {                           // if Stop button is pressed
          t_1ms = 0;                                            // Reset counter to zero
          sec1 = sec2 =48;                                      // Reset all variables to ASCII 48 - char "0"
          min1 = min2 = 48;
          hits1 = hits2 =48;
          break;
       }
       if (Button(&PORTD, 2, 1, 1)) {                           // if Hit button is pressed
          hits1++;                                              // increment hits1 variable
          if (hits1 > 57) {                                     // if hits1 greater then ASCII 57 - char "9"
             hits2++;                                           // increment hits2 variable
             hits1 = 48;                                        // Reset hits1 varible back to zero
          }
          Lcd_chr(2,16,hits1);                                  // Show number of hits on LCD
          Lcd_chr(2,15,hits2);
       }
       if (t_1ms >= 10) {                                       // are varible t_1ms greater or equal to 1000 (1ms * 1000 = 1s)
          PORTB = ~PORTB;                                       // Toggle PORTB every 10ms - TEST!!!
          fr1++;                                                
          if (fr1 > 57) {                                       
             fr2++;                                             
             fr1=48;                                            
             if (fr2 > 57) {                                    
                sec1++;                                         
                fr2=48;                                         
                if(sec1 > 57) {                                 
                    sec2++;                                     
                    sec1=48;                                    
                    if (sec2 > 53) {                          
                       min1++;
                       sec2=48;
                       if (min1 > 57) {
                          min2++;
                          min1=48;
                          if (min2 > 53) {
                             min2=48;
                          }
                       }
                    }
                }
             }
          }
          t_1ms = 0;                                           // Reset t_1ms variable back to zero
          Lcd_Chr(1,16, fr1);                                  // show fr1 on LCD
          Lcd_Chr(1,15, fr2);                                  // show fr2 on LCD
          Lcd_Chr(1,13, sec1);                                 // show sec1 on LCD
          Lcd_Chr(1,12, sec2);                                 // show sec2 on LCD
          Lcd_Chr(1,10, min1);                                 // show min1 on LCD
          Lcd_Chr(1,9, min2);                                  // show min2 on LCD
       }
     }while(1);
}

void main() {
     Init_LCD();                                               // initialize LCD
     Init_timer0();                                            // initialize timer
     ANSEL  = 0;                                               // Configure AN pins as digital
     ANSELH = 0;
     C1ON_bit = 0;                                             // Disable comparators
     C2ON_bit = 0;
     TRISB = 0;                                                // Set all pins of PORTB as output
     PORTB = 0xFF;                                             // Initialize PORTB
     TRISD = 0xFF;                                             // Set all pins of PORTD as input
     fr1 = fr2 = 48;                                           // Set all variables to ASCII 48 - char "0" to show on LCD
     sec1 = sec2 = 48;
     min1 = min2 = 48;
     hits1 = hits2 = 48;
     
  do {
     if (Button(&PORTD, 0, 1, 1)) {                            // if Start button is pressed
        t_1ms = 0;                                             // Set t_1ms to start counting from zero
        Lcd_chr(2,16,hits1);                                   // show hits1 on LCD
        Lcd_chr(2,15,hits2);                                   // show hits2 on LCD
        Lcd_Chr(1,16, fr1);                                    // show fr1 on LCD
        Lcd_Chr(1,15, fr2);                                    // show fr2 on LCD
        Lcd_Chr(1,13, sec1);                                   // show sec1 on LCD
        Lcd_Chr(1,12, sec2);                                   // show sec2 on LCD
        Lcd_Chr(1,10, min1);                                   // show min1 on LCD
        Lcd_Chr(1,9, min2);                                    // show min2 on LCD
        start();                                               // call start() function
     }
  } while(1);
}

Attachments
Proteus simulation.rar
Simulacija
(29.89 KiB) Downloaded 427 times
mikroC code.rar
Kod
(33.02 KiB) Downloaded 436 times
Plexer
Posts: 3
Joined: 16-05-2015, 17:07

Re: PIC + LCD stopwatch

Post by Plexer »

Hvala na linkovima iako ja sam to sve već bio našao i nije odgovaralo onome što meni treba pa sam krenuo u samogradnju...

Problem prvog linka
– Ovdje se dešava nešto slično kao i kod mene. Kad namjestim na npr jednu minutu on odbrojava do nula i tada se pali ledica. E sada kada ja to usporedim sa realnim vremenom dobijem odstupanje od otprilike 12 sekundi na 1 min. Međutim kada promatram ono Proteusovo vrijeme simulacije (slika) onda sve štima.
ps:pogledati attachemnt

Isti takav problem imam sa mojim kodom kada je najmanja promjena vremena koju pratim na LCD-u sekunda, međutim čim cijelu stvar prebacim na stotinke evo ti urnebesa. Više se mjereno vrijeme ne podudara ni sa realnim ni sa ovim iz proteus simulacije.

E i ovdje ima jako puno datoteka, funkcija, deklaracija, header fajlova, ma čuda, i ja mislim da se sve to da skratit. Još se mučim sa dešifriranjem što, gdje, kako, i koga poziva kada.

Problem drugog linka – U proteusu mi izbacuje 2 errora i uopće neće pokrenuti simulaciju i nema nikakvog koda koji bi mogao pogledati da skužim na kojem principu radi

Problem trećeg linka – e ovaj mi nikako ne pase jer ima delay od 17 ms u funkciji u kojoj vrti vrijeme a pošto ja želim da dok je štoperica aktivna mogu u bilo kojem trenutku pritsnuti HIT button i vidjeti promjenu +1 na LCD-u taj delay ne dolazi u obzir.

Dakle prvi link je najbliže nečemu što meni treba iako meni treba obrnuta logika brojanja (povećavanje a ne smanjivanje) ali mi nije jasno to vrijeme koje se razlikuje od štoperice na mobitelu (12 sekundi na 1 min dakle ne radi se o ms da ja to nemogu točno izmjeriti). Zašto je to tako? Dali je to tako samo zbog simulacije i zato jer me Proteus zajebava pa će raditi na fizičkom sklopu ili tu nešto ne štima već sada? Probat ću se još malo pozabavit sa tim kodom i prepravit ga da broji stotinke pa da vidim dali će onda sve ić kvragu kao i kod mene ali za to će mi trebat malo vremena da se snađem u svim tim datotekama.
Attachments
Slika povezana uz problem prvog linka.
Slika povezana uz problem prvog linka.
FB.jpg (13.68 KiB) Viewed 9779 times
User avatar
elektor
Pravi forumaš
Pravi forumaš
Posts: 1588
Joined: 14-10-2008, 20:40
Location: Hrvatska, Rijeka

Re: PIC + LCD stopwatch

Post by elektor »

Prebacite kod da odgovara PICu 18f452 pa dođite do mene drugi tj. da vidimo kako to radi u praksi :wink:
Dzenny_assembly
Napredujem
Napredujem
Posts: 118
Joined: 11-09-2014, 04:03

Re: PIC + LCD stopwatch

Post by Dzenny_assembly »

Plexer, nikad nečeš uspijeti u C-u ili bilo kojem višem jeziku tačno u mikrosekundu odbrojavti vrijeme iz jednostavnog razloga jer nemaš uvid o broju instrukcija koje kompajler generiše. Rutina koja podrazumijeva tajmer i interapt mora biti asemblirana da bi moga tačno u instrukciju izračunati koliko če se kontroker vrtiti u nekoj petlji prije nego odbroji jednu sekundu. Jedino tako možeš postiči programski 100% tačnu rutinu čija če tačnost ovisiti o preciznosti sistemskog takta. Buduči da kvarc kristali daju jako stabilan takt može se napraviti sat koji za nekih mijesec dana odstupi par sekundi. Po meni, to je i više nego zadovoljavajuča tačnost, s obzirom da ni jedan komercijalni sat nije toliko tačan. Izuzev onih "Radio Controlled" satova.

U višim jezicima, bez obzira što koristiš tajmer i interapt, opet se javlja problem jer ne znaš koliko je instrukcija potrebno od pojave interapta do resetovanja tajmera. Ne kažem da je nemoguče i na tvoj način, ali je to "traženje igle u plastu sijena po mrkom mraku."
Maki
Odlično uznapredovao
Odlično uznapredovao
Posts: 766
Joined: 02-07-2012, 12:54

Re: PIC + LCD stopwatch

Post by Maki »

U potpunosti se slažem sa kolegom Dzenny_assembly.
Budući da sam imao sličnih problema, evo nekoliko savjeta. U interrupt rutini postavi da ti stalno mijenja stanje na jedom izlaznom pinu. Na taj pin postavi ledicu. Osciloskopom prati koliko često se ta rutina izvršava. Frekvenicija bi trebala biti konstantna i jako blizu 500 Hz. Ako to nije u redu onda izbaci sve iz programa i ostavi samo interrupt petlju i glavnu petlju. U glavnoj petlji samo zuji, npr. sa nop naredbom. Zatim ponovno prati frekvenciju te bi sad trebala biti 500 Hz.
Time možeš zaključiti da je vrlo vjerovatno problem u glavnoj rutini. Tj. svaki taj kompailer drugačije obrađuje interrupt. Jer koliko se sjećam neki kompaileri ne žele skočiti u interrupt rutini sve dok ne izvrši neku naredbu koja nije assemblerskog tipa. Npr. neke lcd naredbe mogu potrajati oko 1ms, a i više. Zbog toga ti skoči u interrupt rutinu tek kad završi lcd naredbu. No svakako ti je dobro promatrat koliko često ti se tvoja interrupt rutina izvršava. Tako ćeš moći bolje otkriti što je uzrok tvog problema. Samim time ćeš ga i lakše otkloniti.
Dzenny_assembly
Napredujem
Napredujem
Posts: 118
Joined: 11-09-2014, 04:03

Re: PIC + LCD stopwatch

Post by Dzenny_assembly »

Maki slažem se u tome da bi trebao izbaciti sve iz programa i ostaviti samo interup rutinu dok ne postigne željenu preciznost. Medžutim griješiš što se interapta tiče. Npr. kada se tozvoli interupt tajmera programski brojač če "skočiti" na interapt rutinu onog trenutka kada odbrojavanje tajmera krene od nule (overflow).
Izvršavat če interapt rutinu bez obzira gdje se nalazio. Jedino čekanje interapta se može desiti ako se trenutno izvršava interapt rutina neke druge periferije, o tome naravno treba voditi računa pri pisanju koda.
Kontroler "ne zna" da li je kod generisao kompajler ili neko drugi, on samo izvršava assemblerske instrukcije (Ustvari pogrešno je reči i assemblerske, procesor izvršava samo brojčane naredbe. Svaki broj ima svoje značenje).

Evo kako sam ja riješio odbrojavanje vremena:
Pošto se najčešče koristi kvarc kristal od 4MHz ova računica je za tu frekvenciju.
Preskaler tajmera TMR0 je podešen na 1:16 i potreban je jedan registar za odbrojavanje broja prolazaka kroz interapt u koji je upisan br. 244. Dakle tajmer če izvršiti skok na interapt rutinu tek kada odbroji 16x256=4096 impulsa. U interaptu umanjujemo registar u koji je predhodno unijet br. 244. Kada taj registar dodže do nule tada pozivamo neki podprogram u kojem čemo npr. upaliti ledicu i prije povratka iz tog programa registar za odbrojavanje interapt prolazaka napuniti brojem 244.
Poziv podprograma če se dešavati odprilike svake sekunde jer imamo 4096x244=999 424 mikrosekundi.
Nama je naravno potrebno 1 000 000 mikrosekundi da bi smo dobili tačno jednu sekundu.
Po ovoj računici sat bi nam blago brzao. Usporavamo ga tako što nakon ulaska u interapt prije resetovanja tajmera napravimo zadršku sa nekoliko "nop" instrukcija. Sa tim brojem "nop" instrukcija je potrebno malo experimentisati dok se dobije tačno vrijeme.
_heX
Napredujem
Napredujem
Posts: 136
Joined: 29-10-2009, 20:09
Location: Republika Zagorje

Re: PIC + LCD stopwatch

Post by _heX »

Timer0 je jako nezahvalan timer za takve stvari jer moras "manualno" svaki puta setirati vrijednost od koje da tajmer krece. Ako mozes koristi Timer2. Timer2 ima PR2 (period) registar koji se konstantno usporedjuje s TMR2 registrom. Kada se dogodi "match" generira se "interrupt" (ako je omogucen), automatski se resetira TMR2 registar i ciklus krece iznova.

Ti si napisao:

Code: Select all

void interrupt() {                      // Interrupt routine
  if (TMR0IF_bit) {                     // TMROIF_bit > interrupt flag bit
    t_1ms++;                            // Increment counter every 1 ms
    TMR0IF_bit = 0;                     // clear TMR0IF_bit > Set flag back to "0"
    TMR0   = 0x8;                       // Set initial value on TMR0 register (0x8 = 00001000);
  }
}
Moras znati da se tvoja "if (TMR0IF_bit)" ne dogadja neposredno po prekidu. Prije nje se dogadja takozvana "interrupt latency" odnosno kasnjenje (vrijeme od pojave prekida do izvrsavanja instrukcije na adresi 0x004 - prekidni vektor kod PIC-a) koje ako se ne varam kod PIC-a iznosi 2 ili 3 strojna ciklusa (uvjek je konstantno, sto je ok). Na adresi 0x004 izvrsava se kod koji kompajler automatski umece (da on to ne radi ti bi trebao to uraditi manualno i to neovisno o programskom jeziku - znaci vrijedi i za asembler), a koji sprema WREG, STATUS, PCLATH na stog.
E sad, zajeb je sto Timer cijelo to vrijeme broji i kada se sprema izvrsiti tvoja "TMR0 = 0x8;" naredba u TMR0 se vec nalazi neka vrijednost koju ti prebrises/zanemaris s 0x8 - imas gresku, konstantno pomicanje vremena.
Tocan postupak bi bio TMR0 = TMR0 + <offset>; Igraj se s StopWatch-om i podesi "offset".
Izvor greske je i samo koristenje prescalera s TMR0. Kada se upisuje vrijednost u TMR0 prescaler se automatski resetira, a mozda sadrzi vrijednost gdje jos jedan tak fali do inkrementiranja TMR0. Kako vrijednost preskalera nije dostupna tu ni asembler nemoze pomoci.

Moguca tehnika za minimaliziranje greske (8MHz 1:8 prescaler)

Code: Select all

void interrupt isr(void) {
	unsigned char t;

	if(TMR0IF && TMR0IE) {
		t = TMR0 +1;
		while(TMR0 != t) continue;	// blokiraj ovdje 8 ciklusa s 1:8 prescalerom
		TMR0 = TMR0 + <offste>;     // <offset> je 7 za period ravno 1000us (PICC 8.05PL2)
		TMR0IF = 0;
		t_1ms++;
	}
}
A momcima zagovornicima assemblera, predlazem da probate HI-TECH PICC 8.05PL2 c kompajler za midrange PIC. Ja neznam tko su ljudi koji su to napravili, ako uopce jesu ljudi ;). Kazem to jer sam i sam prije njega zagovarao asembler.
Fora je kod C-a sto vas nitko ne prisiljava da koristite kompleksne izraze. Jednostavni izrazi se direktno prevode u strojni (TMR0 = 0; jedna instrukcija / TMR0 = 3; dvije instrukcije - kaj vi mozete bolje?). Optimizator takodjer vidi dal su registri u istoj banci, pa ne dira STATUS reg itd.
Smeta vas poveci kod u pocetku!? Istina nekih ~30 instukcija umetnutih od kompajlera (brisanje memorije itd.). No to je konstantno bio vas program 1k ili 8k instukcija - po meni vise no dobar kompromis uzimajuci u obzi komfor koji nudi visi programski jezik.
Plexer
Posts: 3
Joined: 16-05-2015, 17:07

Re: PIC + LCD stopwatch

Post by Plexer »

Tnx dečki.
Dzenny_assembly
Napredujem
Napredujem
Posts: 118
Joined: 11-09-2014, 04:03

Re: PIC + LCD stopwatch

Post by Dzenny_assembly »

_heX wrote:Kada se upisuje vrijednost u TMR0 prescaler se automatski resetira, a mozda sadrzi vrijednost gdje jos jedan tak fali do inkrementiranja TMR0. Kako vrijednost preskalera nije dostupna tu ni asembler nemoze pomoci.
Hex bojim se da nisi u pravu. Interapt če se desiti onda kada i preskaler i tajmer počnu odbrojavanje od nule.
Ali to nas ne zanima, dovoljno je što znamo da se preskaler resetuje svaki put kada upišemo neku vrijednost u tajmer. Kada se desi interapt dolazi do kašnjenja od dva instrukcijska ciklusa prije nego se krene sa izvršenjem prve instrukcije u interapt rutini. (Procesor interapt doživljava kao klasični poziv podprograma.)
Dakle, kada se krene sa izvršenjem interapt rutine potrebno je samo prebrojati instrukcije koje se nalaze prije instrukcije koja upisuje neku vrijednost u tajmer. Za apsolutnu preciznost bi bilo najbolje koristiti tajmer 1 i preskaler podesiti na 1-1.
vukboban
Pravo uznapredovao :)
Pravo uznapredovao :)
Posts: 212
Joined: 05-12-2008, 17:49
Location: Pancevo

Re: PIC + LCD stopwatch

Post by vukboban »

Post Reply