Timer16
Many of the PIC18 timers have the capability of 16-bit operation and are comprised of two 8-bit registers: TMRxH and TMRxL.
Since it takes multiple instructions to access the two 8-bit registers, you must take care when reading/writing to the timers that the contents are valid and that a rollover hasn't occurred while operating on them. You can set the timer for '16-bit' read/write mode via a bit in the TxCON register (note that the bit name/location can change depending on the device and timer, so check the datasheet)
In 16-bit mode the registers must be accessed in a particular order: to read you must access TMRxL then TMRxH, and to write you use the reverse order TMRxH then TMRxL. The module below contains macro routines to take care of this for you.
timer16.bas module
// // 16-bit timer macros // these are used to enforce reading/writing to the timer 0/1/3/5/7 registers // in the correct order when they are used in 16-bit mode // module timer16 // not all devices support TMR3, TMR5, and/or TMR7, so make support for them // conditional. set the option true to enable them (disabled by default) #option TIMER16_TMR3 = false #option TIMER16_TMR5 = false #option TIMER16_TMR7 = false // macro CheckParam constants // these are from system.bas, but redefined here since they may not be visible // to the module that includes this file public const cpConst = $01, cpVariable = $02, cpArray = $04, cpSize01 = $08, cpSize08 = $10, cpSize16 = $20, cpSize32 = $40, cpInteger = $0100, cpReal = $0200, cpString = $0400, cpChar = $0800, cpBoolean = $1000, etError = 0, etWarning = 1, etMessage = 2, etHint = 3 // // the PIC18 timers are typically set for 8-bit read/write mode by default. // to enable 16-bit mode you have to set the appropriate bit in the TxCON // register. the exact bit depends on the device and the timer, so check // the datasheet. some of the bits to look for are: // T0CON.T08BIT (bit 6) // T1CON.RD16 (usually bit 7) // TxCON.TxRD16 (usually bit 1) // // //----------------------------------------------------------------------------- // write_tmr16() // write timer specified by 'tmr' with the value 'wval' // in 16-bit mode, TMRxH isn't actually updated until TMRxL is written //----------------------------------------------------------------------------- // public macro write_tmr16(tmr, wval) if (tmr = 0) then TMR0H = byte(wval >> 8) TMR0L = byte(wval) elseif (tmr = 1) then TMR1H = byte(wval >> 8) TMR1L = byte(wval) #if (TIMER16_TMR3) elseif (tmr = 3) then TMR3H = byte(wval >> 8) TMR3L = byte(wval) #endif #if (TIMER16_TMR5) elseif (tmr = 5) then TMR5H = byte(wval >> 8) TMR5L = byte(wval) #endif #if (TIMER16_TMR7) elseif (tmr = 7) then TMR7H = byte(wval >> 8) TMR7L = byte(wval) #endif else CheckParam(etError, "unsupported TMR select") endif end macro // //----------------------------------------------------------------------------- // read_tmr16() // reads timer specified by 'tmr', stores it in 'wresult' // in 16-bit mode, reading TMRxL latches TMRxH //----------------------------------------------------------------------------- // public macro read_tmr16(tmr, wresult) if (tmr = 0) then wresult.byte0 = TMR0L wresult.byte1 = TMR0H elseif (tmr = 1) then wresult.byte0 = TMR1L wresult.byte1 = TMR1H #if (TIMER16_TMR3) elseif (tmr = 3) then wresult.byte0 = TMR3L wresult.byte1 = TMR3H #endif #if (TIMER16_TMR5) elseif (tmr = 5) then wresult.byte0 = TMR5L wresult.byte1 = TMR5H #endif #if (TIMER16_TMR7) elseif (tmr = 7) then wresult.byte0 = TMR7L wresult.byte1 = TMR7H #endif else CheckParam(etError, "unsupported TMR select") endif end macro // //----------------------------------------------------------------------------- // incr_tmr16() // add a word value 'wval' to a running timer specified by 'tmr' // // since the timer has to be read LB,HB and written HB,LB we can't do the addition // directly on the timer regs as the math HAS to be done as a word operation // to account for any carry from the low byte, which means doing the LB first. // as is, the code isn't much better than just doing // read_tmr16(0, w) // w = w + 1234 // write_tmr16(0, w) // // if you knew for a fact that the addition would never generate a carry then // this could be shortened to something like: // PRODL = TMR0L // read TMRL to latch TMRH in 16-bit mode // TMR0H = TMR0H + byte(wval >> 8) // TMR0L = PRODL + byte(wval) // // note: to be cycle accurate you'll have to adjust 'wval' to account for the // number of instruction cycles it takes for the code to read and update the // timer registers. the exact adjustment depends on 'wval' (const or variable) // and which bank it's in. it's typ 12 instruction cycles, but it may vary //----------------------------------------------------------------------------- // dim PROD as PRODL.asword // define 16-bit PROD register public macro incr_tmr16(tmr, wval) if (tmr = 0) then PRODL = TMR0L // read TMRL (latches TMRH in 16-bit mode) PRODH = TMR0H PROD = PROD + wval // add wval to current timer TMR0H = PRODH // write timer in proper order (HB,LB) TMR0L = PRODL elseif (tmr = 1) then PRODL = TMR1L // read TMRL (latches TMRH in 16-bit mode) PRODH = TMR1H PROD = PROD + wval // add wval to current timer TMR1H = PRODH // write timer in proper order (HB,LB) TMR1L = PRODL #if (TIMER16_TMR3) elseif (tmr = 3) then PRODL = TMR3L // read TMRL (latches TMRH in 16-bit mode) PRODH = TMR3H PROD = PROD + wval // add wval to current timer TMR3H = PRODH // write timer in proper order (HB,LB) TMR3L = PRODL #endif #if (TIMER16_TMR5) elseif (tmr = 5) then PRODL = TMR5L // read TMRL (latches TMRH in 16-bit mode) PRODH = TMR5H PROD = PROD + wval // add wval to current timer TMR5H = PRODH // write timer in proper order (HB,LB) TMR5L = PRODL #endif #if (TIMER16_TMR7) elseif (tmr = 7) then PRODL = TMR7L // read TMRL (latches TMRH in 16-bit mode) PRODH = TMR7H PROD = PROD + wval // add wval to current timer TMR7H = PRODH // write timer in proper order (HB,LB) TMR7L = PRODL #endif else CheckParam(etError, "unsupported TMR select") endif end macro end module
Example usage:
device = 18F4450 // supports tmr 0, 1 'device = 18F4520 // supports tmr 0, 1, 3 'device = 18F26K22 // supports tmr 0, 1, 3, 5 'device = 18F87K22 // supports tmr 0, 1, 3, 5, 7 // examples to enable support for addtl timers #if (_device = 18F4520) #option TIMER16_TMR3 = true #endif #if (_device = 18F26K22) #option TIMER16_TMR3 = true #option TIMER16_TMR5 = true #endif #if (_device = 18F87K22) #option TIMER16_TMR3 = true #option TIMER16_TMR5 = true #option TIMER16_TMR7 = true #endif include "timer16.bas" dim w as word // load tmr0 with a const value write_tmr16(0, $1234) // load tmr0 with a word value w = $1234 write_tmr16(0, w) // read tmr1, add an offset, and re-write it read_tmr16(1, w) w = w + $1234 write_tmr16(1, w) // same as above, but slightly faster incr_tmr16(1, $1234)