ctimerclockteensy

A mm:ss timer C


Trying to make a timer in C which counts up in mm:ss format, I don't necessarily need to print the value, just have it exist to reference.

The idea is a "time elapsed" clock on a teensy device, every second passed the timer goes up by 1 second, at 60 seconds, the minutes' timer ticks up by 1 and the seconds' timer resets to 0. The timer runs in the background updating itself every second to act as a sort of continuous stopwatch.

Similar to the image below, however, the timer does not start or stop at will, it starts when the program is initialized, and simply increases its counter every second, minutes do not need to convert and can go up to 99, as by this time the program should have ended.

enter image description here

An example is a clock showing time elapsed since beginning the level of a game, showing the user how long they have taken to completed the level.

I've had a go at it below, but because of I'm unfamiliar with how C and C based languages work I'm unsure if I'm even going in the right direction.

int minutes = 0;
int seconds = 0, trigger = 1000;
clock_t start = clock();
do {
  if(seconds == 60) {
    seconds = 0;
    minutes += 1;
  }
  clock_t difference = clock() - start;
  seconds = difference * 1000 / CLOCKS_PER_SEC;
  i++;
} while ( seconds < trigger );

For example, say x seconds have passed;

the timer should return similar to as above.

Windows system.

Can anyone help with this? You can make up whatever variables you want or whatever, for all I know what I have done doesn't even lead anywhere. Thanks in advance.


Solution

  • A timer can be initiated using the Teensy system using its overflows and interrupts. The below code will set up and initiate a timer which will count up in. A struct is used to initiate a boolean value (among other possible things within the program) which can be used to control whether or not the overflow of the timer counts, effectively pausing the timer.

    A function to draw a string

     // Render a string of printable ASCII characters into the screen buffer.
     // Parameters:
     // x - The horizontal position of the top-left corner of the displayed text.
     // y - The vertical position of the top-left corner of the displayed text.
     // character - The ASCII code of the character to render. Valid values range from 
     0x20 == 32 to 0x7f == 127.
     // colour - The colour, FG_COLOUR or BG_COLOUR. If colour is BG_COLOUR,
     the text is rendered as an inverse video block.
    
    void draw_string(int top_left_x, int top_left_y, char *text, colour_t colour) {
        // Draw each character until the null terminator is reached
        for ( uint8_t x = top_left_x, i = 0; text[i] != 0; x += CHAR_WIDTH, i++ ) {
            draw_char(x, top_left_y, text[i], colour);
            // Add a column of spaces here if you want to space out the lettering.
            // (see lcd.c for a hint on how to do this)
        }
    }
    

    Below is a formatting function using the above function to draw a string, the formatting function is used to get the mm:ss layout

    // a formatting function to assist with printing to the screen
    void draw_formatted(int x, int y, const char * format, ...) {
        va_list args;
        va_start(args, format);
        char buffer[1000];
        vsprintf(buffer, format, args);
        draw_string(x, y, buffer, FG_COLOUR);
    }
    

    With the formatting sorted, a struct should be created to initiate the values requiredto operate the timer, and the timer initiated

    // initiates a struct for the timer, which includes a minute, second and 
    // validator accessed using tim
    struct val_store {
        bool timer_validator;
        uint8_t time_passed
        uint8_t min;
        uint8_t sec;
    } tim;
    
    // initiates timer parameters, essentially setting up the timer to be able to function, 
    // sets timer to begin, this should be included in the initial setup of a program
    TCCR1A = 0;
    TCCR1B = 2; 
    TIMSK1 = 1;
    sei();
    tim.timer_validator = true;
    

    Below is the hidden part of the timer, this is the heart, it creates the values which are the basis of this entire question, it is very important, and it will not work unless TCCR1A, TCCR1B, TIMSK1 and sei() are initiated beforehand, the values can vary from 0, 2, 1 respectively, however, the values used in the below timer must be adjusted accordingly using a bit chart

    // Create a volatile global variable called over_flow_count
    volatile unsigned int over_flow_count = 0;
    //  interrupt service routine to process timer overflow
    //  interrupts for Timer 1.
    ISR(TIMER1_OVF_vect) {
        // checks if timer is active or not
        if (tim.timer_validator) {
        over_flow_count++;
        }
    }
    // elapsed time since program start
    // use float instead of double to save memory
    float elapsed_time(void) {
        float current_time = (float)
            ( ( over_flow_count * 65536.0 + TCNT1 ) * 8.0  / 8000000 );
        return current_time;
    }
    

    Finally, the code that works with the above timer to produce an output in mm:ss format is simple due to the timer above doing all the work, all that remains is the formatting. time passed calls the previously created function, which is the total passed time frame, min and sec are then found using division and modulo, and formatted using the previously made formatting function

    tim.time_passed = elapsed_time();
    tim.min = time_passed / 60;
    tim.sec = time_passed % 60;
    draw_formatted(x, y, "Time: %02d:%02d", tim.min, tim.sec);
    

    A pause can be created by using similar

    if (BIT_VALUE(PINB, 0)) {
        // buffer before and after to prevent a single press activating
        // multiple instances of a joystick press, cheap interrupt
        _delay_ms(250);
    
        // pauses timer by setting the boolean to false, preventing the if statement passing
        tim.timer_validator = false;
    
        while(true) {
            clear_screen();
            #Put processes to occur while paused here
    
            // checks to see if the pause should resume
            if (BIT_VALUE(PINB, 0)) {
                break;
            }
    
        // continue timer
        tim.timer_validator = true;
    
        // once again a buffer for good measure
        _delay_ms(250);
        }
    

    If the program is correctly created, the timer should retroactively update itself! Hope it helps!

    Relevant includes, not all includes may be used.

    // includes
    #include <assert.h>
    #include <avr/interrupt.h>
    #include <avr/io.h>
    #include <cpu_speed.h>
    #include <math.h>
    #include <stdarg.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <util/delay.h>