PIC 16F684 van knipperlicht naar... (pagina 4)
Hello World!
Hello World: de zin waarmee elke startersprogramma zich kenbaar maakt. Geen betere zin om op een LCD display te laten zien die aan onze controller is gekoppeld
Voor we het display beschrijven eerst nog enkele aanpassingen aan het programma. Wegens perikelen met Timer0 (en daaruit voortvloeiende onjuistheden - Timer0 zit enkel nog in de chips voor
achterwaartse compatibiliteit) die daardoor niet echt eenvoudig te gebruiken is voor juiste timings, schakelen we over op Timer2 die veel juister en eenvoudiger aan te sturen is.
Ook blijkt de interne oscillator slechts een resolutie van +/- 1% te hebben. Voor een klok is dit niet toereikend.
De aanpassingen zijn te vinden in het schema. Op de pennen 2 en 3 is een 4 MHz kristal en twee condensatoren aanwezig.
De processorinstellingen in het programma moeten we aanpassen:
pragma target OSC XT (bij de interne oscillator was dat pragma target OSC INTOSC_NOCLKOUT).
pragma target clock 4_000_000 ;oscillator frequency pragma target OSC XT ;4MHz xTal pragma target WDT disabled ;no watchdog pragma target MCLR internal ;reset internally |
Timer2. Afgestapt van Timer0 om diverse redenen. Na het laden van Timer0 met een waarde, telt die bij iedere (al dan niet prescaler-gedeelde) puls eentje bij. Bij de overgang van
0xFF (255) naar 0x00 (0) wordt de interruptvlag gezet. De timer telt echter gewoon door. De interrupt wordt direct uitgevoerd maar er gaat tijd verloren voor het saven van belangrijke registers (in
JAL niet zichtbaar maar wel in de *.asm of *.lst file die door de compiler gegenereerd wordt).
In de interruptroutine moet worden gekeken of de interrupt werd veroorzaakt door Timer0 (terug enkele µs verlies) en dan wordt pas de herlaadwaarde aan de timer toegekend. Na die toekenning
duurt het nog 2 cycli (instructietijd * prescaler) eer de timer vanaf die waarde terug herstart. Dit lijkt een µs kwestie (en dat is het ook) maar het is een fout die de interruptfrequentie
omlaag haalt en de berekende waarden kloppen niet meer. Door uitgebreide berekeningen van het verlies aan µs door al die processen en die te verwerken in de berekening van de herlaadwaarde kunnen
toch een juiste timing geven...
Timer2: De teller telt vanaf 0 en de waarde wordt steeds vergeleken met de waarde in PR2. Wanneer die waarde gelijk is, wordt de postscaler met één verhoogd. Na het volledig doorlopen van de postscaler wordt de interruptvlag gezet. De vertragingen in de interruptroutine spelen geen rol meer, de teller telt tot de ingestelde waarde in PR2 en herstart dan vanaf 0.
Er is wel nog een kleine 'valkuil': Bij de berekenede waarde voor PR2 moet steeds 1 afgetrokken worden voor de vertraging/synchronisatie van de teller!
De klokperiode is 1µs (1/4 van de klokfrequentie 4 MHz / 4 geeft 1µs instructieperiode). De prescaler staat op 16 (16µs per telpuls voor Timer2). De herlaadwaarde van de timer
staat op 250 (werkelijke waarde -1!) wat een waarde geeft van 4 ms. De postscaler deelt deze waarde nogmaals door 5 (geeft een 20 ms interrupt).
Die 20 ms is niet toevallig gekozen. We willen namelijk verder bouwen aan dit projectje naar een servotester toe (servo's verlangen een refresh elke 20 ms - 50 Hz).
Verder wordt nog de interrupt voor Timer2 ge-enabled en daar Timer2 deel uit maakt van de periferie moet ook deze periferie-interrupt ge-enabled worden (INTCON_PEIE).
Als volgt nog enkele constanten en variabelen.
;------------------------------------------------------------------------------- ;instellingen voor Timer2 ;Prescaler= 16 ;PostScaler = 5 ;PR2 = 250 - 1 voor herlaadtijd ;Freq = 50 Hz ;Tijd = 0.02 seconden ;------------------------------------------------------------------------------- T2CON_TOUTPS = 0b0100 ;postscaler T2CON_T2CKPS = 0b11 PR2 = 249 ;herlaadwaarde PIE1_TMR2IE = high ;Timer2 interrupt enable INTCON_PEIE = high ;perifierie interrupt enable PIR1_TMR2IF = low ;wis interrupt vlag const byte TimerCounterReload = 50 var byte TimerCounter = TimerCounterReload var volatile byte TimePassed = 0 |
Om data weer te geven door de controller gebruiken we een standaard (HD44780 compatibele) LCD module.
Die module is met de controller verbonden via 6 lijnen: vier voor de data en twee voor de besturingslijnen (handshaking). De laagste 4 bits van poort C dienen voor de data (verbonden met
hoogste 4 databits van de LCD module). C4 is verbonden met de EN lijn van de module en C5 met de RS pin.
De gebruikte poorten worden als uitgang geconfigureerd.
De gebruikte module is er eentje met 2 regels van elk 20 karakters (wordt vastgelegd met de constanten LCD_ROWS en LCD_CHARS).
In dit voorbeeld gebruiken we een variabele om de string 'Hello World' op te bergen, maar daar de data van die variabele hier niet gewijzigd wordt, is het beter om hiervoor een constante te
gebruiken (const byte[] = "Hello World").
Daarna wordt de bibliotheek geladen die alle functies/procedures/instellingen van de LCD module bevat. Na het laden wordt het het LCD geïnitialiseerd.
Een andere interessante bibliotheek wordt ook geladen: 'print'. Deze bibliotheek bevat verschillende procedures om op een eenvoudige manier data op een scherm (of via een seriële verbinding) weer te geven.
Het scherm wordt gewist en de tekst wordt op het scherm weergegeven.
;------------------------------------------------------------------------------- ;LCD declaratie en initialisatie ;------------------------------------------------------------------------------- ;Volgende constanten moeten gedeclareerd worden: const byte LCD_ROWS = 2 -- 1, 2 or 4 lines const byte LCD_CHARS = 20 -- 8, 16 or 20 chars per line ;Alisassen voor de handshake-lijnen: alias lcd_rs is pin_c5 -- cmd/data select alias lcd_en is pin_c4 -- trigger pin_C5_direction = output pin_C4_direction = output ;Aliassen voor de vier datalijnen: alias lcd_dataport is portC_low -- 4 databits pin_C0_direction = output pin_C1_direction = output pin_C2_direction = output pin_C3_direction = output var byte World[11] = "Hello World" ;laad de eigenlijke bibliotheek include lcd_hd44780_4 ;en ionitialiseer het display lcd_init() -- init the lcd controller ;We laden meteen een andere interessante bibliotheek om eenvoudig tekst ;op het display te krijgen include print ;Om het scherm toch iets nuttig te laten zien, tonen we de verlopen seconden var dword dwSeconds = 0 ;en we zetten de tekst op het scherm na dit voor alle zekerheid eerst te ;wissen: lcd_clear_screen() print_string(lcd, World) |
In de interruptprocedure wordt nu enkel ingegaan op een interrupt die door Timer2 wordt veroorzaakt (elke 20 ms). Elke seconde wordt dit aan de mainloop doorgegeven via de variabele TimePassed.
;------------------------------------------------------------------------------- ;INTERRUPT PROCEDURE ;------------------------------------------------------------------------------- procedure Timer0Interrupt is pragma interrupt if(PIR1_TMR2IF)then PIR1_TMR2IF = low ;20ms ;laat dit 50 keer gebeuren voor 1 seconde TimerCounter = TimerCounter - 1 if(TimerCounter == 0)then TimerCounter = TimerCounterReload ;herlaad de teller TimePassed = 1 ;tijd is verstreken end if end if end procedure |
In dit voorbeeld verstrijkt er wel wat tijd vooralleer de mainloop wordt gestart (LCD wordt geïnitialiseerd en tekst op het scherm gezet). Vandaar dat we wachten om de interrupt in te schakelen tot vlak voor die mainloop.
Die mainloop is op zich heel eenvoudig: de led knippert om de seconde en op de 2° regel van het scherm wordt de verlopen tijd (in seconden) weergegeven.
;------------------------------------------------------------------------------- T2CON_TMR2ON = high ;turn timer2 on; INTCON_GIE = high ;enable interrupts ;------------------------------------------------------------------------------- forever loop if(TimePassed > 0)then ;1 seconde is verstreken TimePassed = 0 ;wis de vlag Led = !Led ;zet de led aan of uit ;zet de teller op het LCD lijn 2, 1° positie lcd_cursor_position(1,0) dwSeconds = dwSeconds + 1 print_dword_dec(lcd, dwSeconds) end if ;hier tijd voor andere zaken uit te voeren ;... end loop ;------------------------------------------------------------------------------- |
(pagina 3) ...PIC 16F684 van knipperlicht naar... (pagina 5)
Links