Timer with Sprite Sheets

Timer example with Sprite Sheets

Download the tutorial files below if needed:

The internal RTC keeps track of the date and time through the CLOCK or CALENDAR objects but some applications such as engine hours, service intervals, or current run timers are difficult to tie in with those. The example below will show how to add a run timer then break it down to individual seconds, minutes, and hours so that a customized numeral image could be used.

The design below implements a 100 hour timer. The DIVISION and MODULUS objects are used to break apart the second counter into the correct hour/minute/second digits.


Each of the numerals is based on the same sprite sheet as shown below:

It is helpful to use a mono-spaced font so that each digit has the same width and height. The SPRITE objects then break this single image down so that the index points to the correct numeral. An index of 0 would draw the first region corresponding to the numeral 0. An index greater than 9 would not be valid as that exceeds the bounds of the image.

Since each digit is now a graphical element it can be created with multiple color gradients for shading or 3D effects. The characters can be tilted or even rotated if the screen were to be used in a portrait orientation.

The math behind decoding the second timer into each digit place should be somewhat straightforward. The timer is divided down and then the modulus is used to break apart individual places. The advantage to this method is that everything occurs synchronously. If multiple counters were used for seconds, minutes, and hours the transitions and roll overs may require additional synchronization logic. Also remember that since all math is integer based division operations throw away any remainders so 1.1 = 1.9 = 1.

This particular example rolls over at the 100 hour mark so the maximum displayed time would be 99:59:59. If more hours are required an additional SPRITE object would be needed to represent HOUR_HUNDREDS. The SEC_TIMER counter range would also need to be raised from 360,000 to 3,600,000. Lastly the division and modulus objects would need to be added to break apart the hundreds place value out of the total count.

The tutorial file is set up so that the SEC_TIMER can be easily adjusted during simulation. Simply double-clicking the net TEST_VALUE and applying a new simulation value then clicking anywhere on the display will load the counter with a new time. A value of 3590 would display a time of 59:50 and then proceed to count up to 1 hour and show all the minutes and seconds rolling over.

Once the timer example is well understood additional features can be easily incorporated. For a timer that doesn’t reset every time the system is powered up a MEMORY object can be added that tracks the last updated value of the SEC_TIMER. On power up the LOAD pin can be pulsed with the MEMORY object value tied to the COUNTER object value. This could be used to track engine hours or maintenance intervals. Adding a button or hidden region that must be pressed or held down for some amount of time to reset the counter could then allow for technicians to reset the stored times during maintenance.

Further optimization could be handled by sharing the same SPRITE objects with each of the different timers. Separate counters for engine run time, maintenance, and current up time could feed 3 separate BUFFER objects all tied to the SEC_TIMER net. Depending on which BUFFER was enabled the digits would represent each of those values. A button could be added to cycle display of each of these values by incrementing a small counter tied to a DECODE object. When the button is pressed the counter steps the DECODE object to the next enable pin of each BUFFER so that the same screen space can be reused for each independent value.

As a final step all of the math operations could be implemented in the CODE script further simplifying the design and allowing for easier re-use.

void Init(){
void Update(){
    SEC_ONES = SEC_TIMER % 10;
    SEC_TENS = (SEC_TIMER % 60) / 10;
    MIN_ONES = (SEC_TIMER / 60) % 10;
    MIN_TENS = ((SEC_TIMER / 60) % 60) / 10;
    HOUR_ONES = (SEC_TIMER / 3600) % 10;
    HOUR_TENS = (SEC_TIMER / 3600) / 10;