PIC 16F684 van knipperlicht naar... (pagina 2)

Schema      JAL code      HEX file


In het vorig voorbeeld werd in een lus geteld tot er één seconde verstreken was.  De processor is daar volop mee bezig (hij telt tot 1000000 - één instructie neemt 1/4 van de klokfrequentie in beslag).  Tijdens dat tellen is de processor tot niets anders in staat...

Een elegantere oplossing is door gebruik te maken van één van de timers in de chip aanwezig.
De eerste timer is TIMER0.  Dat is een 8 bits timer/teller die zowel door de interne als door een externe klok kan bediend worden.  De timer is een manusje-van-alles en alle gegevens zijn in de (heel uitgebreide) datasheet van de PIC te vinden.

In het programma is een bijkomend hoofdstuk gemaakt met daarin de instellingen voor Timer0:

  ;-------------------------------------------------------------------------------
;instellingen voor Timer0
;-------------------------------------------------------------------------------
const byte Timer0Reload = 0x06
const byte TimerCounterReload = 250
var byte TimerCounter = TimerCounterReload

;geen interrupt
INTCON_T0IE = low
OPTION_REG_T0CS = low ;Timer0 timer mode
OPTION_REG_PSA = low ;prescaler op Timer0
;reset alle prescaler bits en zet daarna de prescaler op 011 (=/16)
OPTION_REG = (OPTION_REG & 0xF8) | 0x03
TMR0 = Timer0Reload
;-------------------------------------------------------------------------------
 

Timer0Reload is gedefiniëerd als constante.  Dat is de waarde waarmee Timer0 moet herladen worden.  Timer0 is een incrementele timer, dat wil zeggen dat bij iedere puls de teller met één verhoogt wordt.  Bij het bereiken van de maximale waarde (0xFF of 255 decimaal) gaat de teller terug over naar 0.  Bij die overgang wordt de INTCON_T0IF vlag gezet (die softwarematig terug moet gereset worden) en telt de teller terug van 0 tot 255 en opnieuw.  Als vrijloop teller kunnen we enkel maar gebruik maken van de prescaler om de tijd aan te passen en zitten we beperkt met de mogelijke tijdsinstellingen.
Gelukkig kan Timer0 met een waarde geladen worden.  Er wordt dan vanaf die ingestelde waarde geteld tot 255 waarna de INTCON_T0IF gezet wordt.  Hier moet op gereageerd worden en de ingesteld waard moet opnieuw ingevoerd worden (anders telt de teller gewoon van 0 tot 255 verder).  Timer0Reload is de waarde waarmee Timer0 moet herladen worden (daarom ook gedefiniëerd als constante).

De tijd die ingesteld kan worden hangt van verschillende factoren af (processorfrequentie / prescaler / herlaadwaarde) en vrij complex om te berekenen.  Gelukkig zijn er online enkele goeie rekenmachines voor de verschillende timers van een PIC (zoals PIC Timer Calculator and Source Code Generator).
Om het voor mezelf gemakkelijk te maken, gebruik ik een lijst waar alle mogelijke waarden zijn ingevuld.

Verder zijn er nog enkele instellingen nodig om de timer te laten werken: geen interrupt, de timer in TIMER mode zetten (anders werkt hij als COUNTER) en de prescaler aan de timer koppelen (is anders aan de watchdog gekoppeld).

Daar alle vlaggen in OPTION_REG reeds gezet zijn en we de waarde van de prescaler er nog moeten bijvoegen, wordt hier een vlug trucje uitgehaald.  De drie laagste bits van OPTION_REG zijn de waarden voor de prescaler.  Om die drie laagste bits te resetten, wordt een AND bewerking uitgevoerd op OPTION_REG en de hexadecimale waarde 0xF8 (binair 0b11111000) waardoor de laagste drie bits zeker nul worden.  Nadien wordt met de bekomen waarde een OR bewerking uitgevoerd met 0x03 waardoor de prescaler op 16 staat.  In JAL is dit op één regeltje in één bewerking te schrijven.

Door al die instellingen staat de timer nu ingesteld op 4 ms.  Een waarde instellen om direct één seconde te krijgen, lukt niet.
Waarom dan 4 ms? De meeste timerwaarden zijn machten van 2 en daardoor verre van decimaal (zie lijst). De enige decimale waarden waarmee gemakkelijk kan gerekend worden zijn 250 µs, 500 µs, 1 ms, 2 ms en 4 ms. Bij een kleinere waarde dan 4 ms moeten we een 16 bits tussenteller gebruiken om tot 1 seconde te komen (2 ms * 255 = 510 ms maximum). Bij 4 ms moeten we een tussenteller van 249 tot 0 laten tellen om 1 seconde te hebben.

  ;-------------------------------------------------------------------------------
forever loop
  if
(INTCON_T0IF)then
    
;T0IF is geset, 4 ms zijn verlopen
    
TMR0 = Timer0Reload ;herlaad Timer0
    
INTCON_T0IF = low ;reset Timer0 interrupt vlag
    ;laat dit 250 keer gebeuren voor 1 seconde
    
TimerCounter = TimerCounter - 1
    
if(TimerCounter == 0)then
      
TimerCounter = TimerCounterReload ;herlaad de teller
      
Led = !Led ;zet de led aan of uit
    
end if
  end if
  
;hier tijd voor andere zaken uit te voeren
  ;...
end loop
;-------------------------------------------------------------------------------
 

Het eigenlijke programma houdt niet veel meer in dan te kijken of de 4 ms verstreken zijn (INTCON_T0IF wordt dan gezet). Verminder de teller (met de startwaarde 249). Wanneer die teller op nul staat, is er één seconde verstreken en moet de led aan- of uitgezet worden.

Dat aan- en uitzetten van de led gebeurd hier op een heel eenvoudige manier: de waarde van de poort wordt gelezen, geďnverteerd en terug op de poort gezet. Bij een resistieve belasting (zoals dat ledje) kan dat geen kwaad. Bij capacitieve of inductieve belastingen is het verstandiger om een tussenvariabele te gebruiken.

Na de controle van de INTCON_T0IF vlag heeft de processor enorm veel tijd om nuttige zaken te doen (de drie puntjes in het programma) en hoeft hij zich niet meer bezig te houden met 1 seconde te wachten.

 


(pagina 1) ...PIC 16F684 van knipperlicht naar... (pagina 3)


Links

JALEdit de editor om JAL code te schrijven
JALlib de bibliotheken (en voorbeelden) en de compiler
MPLab IDE de ontwikkel- en PicProgrammeromgeving
Microchip de site van de PIC's
JalList Hulp en hopen voorbeelden