Framerate Timing

February 06, 2008 | 2 Minute Read

I’ve been trying to track down an annoying timing issue in my main loop.

My target is 20 frames/second (capped), which means 50ms between frames. I start a 50ms countdown timer at the beginning of the main loop, then process messages in a tight loop until the timer expires. When time’s up, I process events tied to the framerate once, then reset the timer and go back to processing messages. The problem is that the message processing is periodically skipped.

;; ----------------------------------------------
;;  void Ifos.main()
;;  Main execution loop.  This routine cycles endlessly, updating the state of
;;  the application and responding to external events.
   ; See if a new message has arrived.
   movlw    Modbus.kState_MsgQueued
   cpfseq   Modbus.State            ; is a MODBUS message waiting for us?
     bra    doWork                  ; no, do normal stuff
   ; We received a message, so do something with it.
   SetTableBase Modbus.VTable       ; point to virtual function table
   call     Modbus.dispatchMsg      ; process message and build reply
   call     Modbus.replyMsg         ; send reply
   ; See if we've entered a vertical blank.
   call     Clock.isAwake           ; has 50ms elapsed?
   bnc      Ifos.main               ; no, go check for incoming messages again
   ;  do useful stuff here
   ; Reset the vertical blank timer.
   btg      PORTA, RA3              ; toggle I/O for debugging
   SetAlarmMS 50
   bra      Ifos.main

To aid debugging, I replaced the frame event processing with a simple I/O toggle of RA3, producing a straightforward trace (not to scale):

Timing trace It’s clear something is shortening the cycle on a regular basis (red lines). The actual number of pulses between each pair of red lines is about 22—close enough to 20 to seem significant, except that the total time is greater than 1s. I might expect it to be less, if the code were trying to ensure a minimum of 20fps, but it’s written to cap the framerate, instead.

It’s particularly strange since the timer is reset relative to when it expires, not some fixed beat. That is, when the timer hits zero, it’s reset to 50, not 50 minus the time spent processing messages.

I’ve been looking at this for a while and not making much progress. I started down this path trying to debug a different problem (something wonky in the message handling state machine), but that’s obviously going nowhere. My only thought is that the timeout comparison is sketchy… I need to investigate actual countdown values to see if they appear suspicious.