Timer32
This module implements a 32-bit usec timer using TMR0. It requires that the TMR0 prescaler be set for the timer to count directly in 1usec intervals, so it works for clock settings of 4, 8, 16, 32, and 64MHz.
Sample Code (single-shot mode)
include "timer32.bas" dim elap_time as longword timer32.init() timer32.start() <code to time here> elap_time = timer32.read()
Sample Code (continuous-run mode)
include "timer32.bas" dim start_time, elap_time as longword timer32.init() timer32.start() start_time = timer32.read() <code to time here> elap_time = timer32.read() - start_time start_time = timer32.read() <more code to time here> elap_time = timer32.read() - start_time
Sample Code (run code for a time period)
include "timer32.bas" dim elap_time as longword dim LED as PORTB.0 timer32.init() // toggle LED for 100ms low(LED) timer32.start() repeat toggle(LED) elap_time = timer32.read() until (elap_time >= 100000) // 100ms = 100000us
timer32 Module
{ ***************************************************************************** * Name : timer32.bas * * Author : Jerry Messina * * Date : 05/30/17 * * Version : 1.0 * * Notes : 1.0 Initial release * ***************************************************************************** } module timer32 // // 32-bit usec timer using TMR0 counts 4294967296us (4294secs, or 71min) with 1us resolution // set TMR0 to count the instruction clock (FOSC/4) // if the clock is a power of 2 frequency between 4MHz and 64MHz (ie 4/8/16/32, or 64MHz) then // prescaler can be set to 1,2,4,8,16 (_clock/4) to give 1us counts // when TMR0 overflows incr usecs_hw (high word of the 32bit usec counter) and clear T0IF // TMR0 continues to count, so you have 65535us (~65ms) to service the interrupt // // usage: // timer32.init(event_handler) // timer32.start() // // continuous mode (difference between two reads, timer continues running): // dim start_time, elap_time as longword // start_time = timer32.read() // <code to time here> // elap_time = timer32.read() - start_time // <more code to time here> // elap_time = timer32.read() - start_time // // single-shot mode (restart timer): // timer32.start() // <code to time here> // elap_time = timer32.read() // // even though the timer is counting usecs, there's about 30 cycles of overhead in continuous mode // so that will limit the accuracy compared to single-shot mode // #if not(_clock in (4,8,16,32,64)) then #error "this module requires a clock of 4MHz-64MHz in binary steps" #endif // select interrupt priority for TMR0 // 1 = ipLow, 2 = ipHigh (default) #option TIMER32_PRIORITY = 2 const INTR_PRIORITY = TIMER32_PRIORITY // TMR0 interrupt register bits dim TMR0IF as INTCON.bits(2), TMR0IE as INTCON.bits(5), TMR0IP as INTCON2.bits(2) // event called by the isr when TMR0 overflows every 65536us type event_t = event() dim OnEvent as event_t // timer overflow count // 32-bit usec timer upper word is incremented by overflow isr, lower word is TMR0 count value dim usecs_hw as word // macros to write/read tmr0 in 16-bit mode public macro write_tmr0(wval) TMR0H = byte(wval >> 8) TMR0L = byte(wval) end macro public macro read_tmr0(wresult) wresult.byte0 = TMR0L wresult.byte1 = TMR0H end macro // T0CON register bits const T0PS0 = 0, T0PS1 = 1, T0PS2 = 2, PSA = 3, T0SE = 4, T0CS = 5, T08BIT = 6, TMR0ON = 7 // tmr0 overflow interrupt interrupt tmr0_isr(INTR_PRIORITY) TMR0IF = 0 // clear timer IF usecs_hw = usecs_hw + 1 // incr counter upper word OnEvent() // call user event (if set) end interrupt // stop timer public sub stop() T0CON.bits(TMR0ON) = 0 TMR0IE = 0 TMR0IF = 0 end sub // start timer public sub start() // stop the timer (in case it's running) stop() // init usecs_hw = 0 write_tmr0(0) TMR0IE = 1 enable(tmr0_isr) // and start timer T0CON.bits(TMR0ON) = 1 end sub // init TMR0 to count 1usec increments public sub init(event_handle as event_t = 0) // make sure timer isn't running stop() // setup TMR0 T0CON = 0 // clear all t0con bits, setting defaults to 0 T0CON.bits(T08BIT) = 0 // 0 = Timer0 is configured as a 16-bit timer/counter T0CON.bits(T0CS) = 0 // 0 = Internal instruction cycle clock (CLKOUT) T0CON.bits(PSA) = 0 // 0 = Timer0 prescaler is assigned select (_clock) case 4 // 1:1 (no prescaler) T0CON.bits(PSA) = 1 // 1 = Timer0 prescaler is not assigned case 8 // 1:2 prescaler T0CON.bits(T0PS2) = 0 T0CON.bits(T0PS1) = 0 T0CON.bits(T0PS0) = 0 case 16 // 1:4 prescaler T0CON.bits(T0PS2) = 0 T0CON.bits(T0PS1) = 0 T0CON.bits(T0PS0) = 1 case 32 // 1:8 prescaler T0CON.bits(T0PS2) = 0 T0CON.bits(T0PS1) = 1 T0CON.bits(T0PS0) = 0 case 64 // 1:16 prescaler T0CON.bits(T0PS2) = 0 T0CON.bits(T0PS1) = 1 T0CON.bits(T0PS0) = 1 end select // set intr priority #if (TIMER32_PRIORITY = 2) then TMR0IP = 1 // high priority #else TMR0IP = 0 // low priority #endif // set event (optional) OnEvent = event_handle end sub // read 32-bit timer // don't stop the timer... just read it until there are two successive // equal readings of the high word to ensure there isn't a false value // because of the interrupt public function read() as longword repeat result.word1 = usecs_hw // read high word read_tmr0(result.word0) // get the low word value from the tmr until (result.word1 = usecs_hw) // make sure we weren't interrupted end function // module init OnEvent = 0 end module
Test program
device = 18F25K22 clock = 64 // 4, 8, 16, 32, or 64 config FOSC = INTIO7, PLLCFG = OFF, PRICLKEN = OFF, FCMEN = OFF, IESO = OFF // timer32 defaults to high-priority (2) '#option TIMER32_PRIORITY = 1 include "timer32.bas" include "usart.bas" include "convert.bas" // define an event that's called when the timer overflows (every 65536us) dim LED as PORTB.0 event event_handler() toggle(LED) end event dim start_time as longword, elap_time as longword dim w as word // set 18F25K22 osc public sub set_clock() // select primary osc (depends on OSC config bits) #if (_clock = 4) OSCCON = $50 // primary osc, 4MHz int osc #elseif (_clock = 8) or (_clock = 32) OSCCON = $60 // primary osc, 8MHz int osc #elseif (_clock = 16) or (_clock = 64) OSCCON = $70 // primary osc, 16MHz int osc #else #error "unsupported _clock freq setting" #endif #if (_clock = 32) or (_clock = 64) OSCTUNE.bits(6) = 1 // PLL enabled (x4) #else OSCTUNE.bits(6) = 0 // pll off #endif delayms(10) // wait for clock to stabilize end sub main: set_clock() usart.setbaudrate(br115200) usart.write("clock = ", dectostr(_clock), "MHz", #13, #10) // setup timer (no event) timer32.init() // measure overhead // minimal overhead using start()/read() timer32.start() elap_time = timer32.read() usart.write("overhead start-read = ", dectostr(elap_time), "us", #13, #10) // there's about 32 instruction cycles of overhead on reading the timer twice, // so at 64MHz that's about 2usecs start_time = timer32.read() elap_time = timer32.read() - start_time usart.write("overhead read-read = ", dectostr(elap_time), "us", #13, #10) usart.write(#13, #10) // example 1: run code for a time period // toggle LED for 100ms low(LED) timer32.start() repeat toggle(LED) elap_time = timer32.read() until (elap_time >= 100000) // 100ms = 100000us LED = 0 usart.write("LED toggled for ", dectostr(elap_time), "us", #13, #10) usart.write(#13, #10) // setup timer to use event timer32.init(event_handler) // example 2: measure delayus calls w = 1 while (w <= 10000) timer32.start() delayus(w) elap_time = timer32.read() usart.write(dectostr(w), "us start-read = ", dectostr(elap_time), "us", #13, #10) start_time = timer32.read() delayus(w) elap_time = timer32.read() - start_time usart.write(dectostr(w), "us read-read = ", dectostr(elap_time), "us", #13, #10) w = w * 10 end while usart.write(#13, #10) // example 3: measure delayms calls w = 1 while (w <= 10000) timer32.start() delayms(w) elap_time = timer32.read() usart.write(dectostr(w), "ms start-read = ", dectostr(elap_time), "us", #13, #10) start_time = timer32.read() delayms(w) elap_time = timer32.read() - start_time usart.write(dectostr(w), "ms read-read = ", dectostr(elap_time), "us", #13, #10) w = w * 10 end while usart.write(#13, #10)
Sample test output
clock = 64MHz overhead start-read = 0us overhead read-read = 2us LED toggled for 100002us 1us start-read = 1us 1us read-read = 3us 10us start-read = 10us 10us read-read = 12us 100us start-read = 100us 100us read-read = 102us 1000us start-read = 1000us 1000us read-read = 1002us 10000us start-read = 10000us 10000us read-read = 10002us 1ms start-read = 1001us 1ms read-read = 1003us 10ms start-read = 9997us 10ms read-read = 9998us 100ms start-read = 99965us 100ms read-read = 99969us 1000ms start-read = 999655us 1000ms read-read = 999657us 10000ms start-read = 9996545us 10000ms read-read = 9996549us clock = 8MHz overhead start-read = 4us overhead read-read = 13us LED toggled for 100006us 1us start-read = 17us 1us read-read = 25us 10us start-read = 16us 10us read-read = 25us 100us start-read = 105us 100us read-read = 114us 1000us start-read = 1005us 1000us read-read = 1013us 10000us start-read = 10005us 10000us read-read = 10013us 1ms start-read = 1011us 1ms read-read = 1020us 10ms start-read = 10011us 10ms read-read = 10020us 100ms start-read = 100027us 100ms read-read = 100051us 1000ms start-read = 1000244us 1000ms read-read = 1000252us 10000ms start-read = 10002367us 10000ms read-read = 10002392us