Thread: timing algorithm, assistance required,

  1. #1
    Registered User
    Join Date
    Feb 2015
    Location
    Glasgow
    Posts
    4

    timing algorithm, assistance required,

    hi all,

    I'm programming a Pic, and have it up and running bare code, but i wish to control three relays, one closes every three seconds, for 1 second duration, another during the first relays activation only for 0.1 of a second. The third closes for 120 seconds, every seven days..... i am reverting to drawing diagrams, but any assistance greatly welcomed as i am struggling a bit on how to approach this.

    many thanks

    SB
    Last edited by Steven Boyle; 02-03-2015 at 09:58 AM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,172
    7 days in 0.1 second intervals is 6,048,000

    You can either do
    Code:
    for ( int i = 0 ; i < 6048000 ; i++ ) {
      sleep(0.1);
    }
    Or arrange an interrupt every 0.1 sec and call
    Code:
    volatile int counter = 0;
    void isr ( void ) {
      if ( ++counter == 6048000 )
        counter = 0;
    }
    The rest is just working out what value(s) represent which state(s)
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Feb 2015
    Location
    Glasgow
    Posts
    4
    Quote Originally Posted by Salem View Post
    7 days in 0.1 second intervals is 6,048,000

    You can either do
    Code:
    for ( int i = 0 ; i < 6048000 ; i++ ) {
      sleep(0.1);
    }
    Or arrange an interrupt every 0.1 sec and call
    Code:
    volatile int counter = 0;
    void isr ( void ) {
      if ( ++counter == 6048000 )
        counter = 0;
    }
    The rest is just working out what value(s) represent which state(s)
    thanks salem,

    its more a question of structure as timing is easy, the inter-relationship of the actuation of the relays is my concern,

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,907
    The smallest period of time you must deal with is 0.1s, so you need to count everything in tenths of a second.

    Salem showed you how to count out a 7-day period. Now, you need to apply that to your other relays.

    Each relay has a period, the time it takes to repeat the on-off pattern. Thankfully you have a simple pattern, which repeats after just a single on and single off.

    Relay 1 has a 3 second period, which is ??? tenths of a second.
    Relay 2 also has a 3 second period, which is ??? tenths of a second.
    Relay 3 has a 7 day period, which we know is 6048000 tenths of a second.

    So you need one counter for the 3 day period relays and one counter for the 7 day period relay. Figure out what values for each counter should correspond to each relay opening/closing; Perhaps draw a timeline and label the open/close points and timer reset point. Then, constuct a simple if/else-if chain for each timer that covers all the open/close and reset values.

  5. #5
    Registered User
    Join Date
    Feb 2015
    Location
    Glasgow
    Posts
    4
    i do have a concern in that my hardware is outputting to lcd every second, the short time cycles will not allow the per sec update of the lcd, so i need something a bit more refined i fear,,

    steven
    Last edited by Steven Boyle; 02-04-2015 at 01:57 AM.

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Your main time interval is three seconds. Every seven days or 7246060/3 = 201,600 intervals, the third relay is active for 120/3 = 40 intervals. That is, the third relay is deactive for 201,560 intervals, then active for 40.

    During each three-second interval, you have the following events:

    +0.0 seconds: Update LCD
    +0.0 seconds: Activate latch 1
    +0.0 seconds: Activate latch 2
    +0.1 seconds: Deactivate latch 2
    +1.0 seconds: Update LCD
    +1.0 seconds: Deactivate latch 1
    +2.0 seconds: Update LCD

    The next interval is just like above, just three seconds later.

    The first 201,558 intervals are as as above. The 201,559th interval, you also activate latch 3 at +0.0 seconds. The next 38 intervals are as above. The 201,599th interval, you also deactivate latch 3 at +0.0 seconds. Then, you start from the beginning.

    Depending on the relative offsets of the latch triggers and LCD updates, you'll want to adjust the details above, but as I see it, it's pretty straightforward.

    I would personally also use a reliable RTC, and if supported by your PIC and the RTC, use a low-power sleep to wait for the next interesting event to happen. It is often easier to consider the intervals rather than the absolute time when something happens, but you need an RTC or other reliable clock source to sync to (using the 1Hz output many RTCs provide), to avoid drifting (because the work done at each event takes a bit of time, and we don't know exactly how long, and it might even vary).

    Questions?
    Last edited by Nominal Animal; 02-04-2015 at 03:04 AM.

  7. #7
    Registered User
    Join Date
    Feb 2015
    Location
    Glasgow
    Posts
    4
    thanks for input,

    i have realised my application requires flexibility, in that the on periods, and the time also between events for each relay has to be flexible, so i will have to use seconds, the application i have may need to be 2 seconds on, etc. i will post my first attempt, i suppose i am realising the problem as i am going along here, but thanks for everyones input thus far, its a great help,

    steven

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    How about something like the following simulator code:
    Code:
    #include <stdio.h>
    
    /* Let's assume we use 0.1 second precision. */
    #define PER_SEC         10
    
    /* LCD updates. Happen at the start. */
    #define LCD_PERIOD      (PER_SEC)
    #define LCD_UPDATE      (0)
    
    /* Relay states. */
    #define RELAY_WAIT      0   /* Before activation */
    #define RELAY_ON        1   /* Active */
    #define RELAY_OFF       2   /* After activation */
    
    /* Relay 1 state durations. */
    #define RELAY_1_WAIT    (1*PER_SEC)
    #define RELAY_1_ON      (1*PER_SEC)
    #define RELAY_1_OFF     (3*PER_SEC - RELAY_1_ON - RELAY_1_WAIT)
    
    /* Relay 2 state durations. */
    #define RELAY_2_WAIT    (15*PER_SEC/10)
    #define RELAY_2_ON      (1*PER_SEC/10)
    #define RELAY_2_OFF     (3*PER_SEC - RELAY_2_ON - RELAY_2_WAIT)
    
    /* Relay 3 state durations. */
    #define RELAY_3_WAIT    (7*24*60*60*PER_SEC - RELAY_3_ON - RELAY_3_OFF)
    #define RELAY_3_ON      (120*PER_SEC)
    #define RELAY_3_OFF     (0)
    
    int main(void)
    {
        long    lcd_ticks = LCD_UPDATE;
        long    relay_1_ticks = RELAY_1_WAIT;
        long    relay_2_ticks = RELAY_2_WAIT;
        long    relay_3_ticks = RELAY_3_WAIT;
        long    ticks, sleepticks;
        char    relay_1_state = RELAY_WAIT;
        char    relay_2_state = RELAY_WAIT;
        char    relay_3_state = RELAY_WAIT;
    
        sleepticks = 0L;
    
        while (1) {
            /* Obtain actual elapsed time using
             *     prev_timer = curr_timer;
             *     curr_timer = timer();
             *     ticks = curr_timer - prev_timer;
             * where timer() is an RTC at PER_SEC resolution.
             * Note that you should check for wraparound (in ticks),
             * the following code assumes ticks is >= 0 and small.
             *
             * Here, we just assume our sleep was perfect.
            */
            ticks = sleepticks;
    
            lcd_ticks -= ticks;
            if (lcd_ticks <= 0L) {
                printf("Update LCD.\n");
                lcd_ticks += LCD_PERIOD;
            }
    
            relay_1_ticks -= ticks;
            while (relay_1_ticks <= 0L)
                switch (relay_1_state) {
                case RELAY_WAIT:
                    relay_1_state = RELAY_ON;
                    relay_1_ticks += RELAY_1_ON;
                    printf("Activate relay 1.\n");
                    break;
                case RELAY_ON:
                    relay_1_state = RELAY_OFF;
                    relay_1_ticks += RELAY_1_OFF;
                    printf("Deactivate relay 1.\n");
                    break;
                default:
                    relay_1_state = RELAY_WAIT;
                    relay_1_ticks += RELAY_1_WAIT;
                }
    
            relay_2_ticks -= ticks;
            while (relay_2_ticks <= 0L)
                switch (relay_2_state) {
                case RELAY_WAIT:
                    relay_2_state = RELAY_ON;
                    relay_2_ticks += RELAY_2_ON;
                    printf("Activate relay 2.\n");
                    break;
                case RELAY_ON:
                    relay_2_state = RELAY_OFF;
                    relay_2_ticks += RELAY_2_OFF;
                    printf("Deactivate relay 2.\n");
                    break;
                default:
                    relay_2_state = RELAY_WAIT;
                    relay_2_ticks += RELAY_2_WAIT;
                }
    
            relay_3_ticks -= ticks;
            while (relay_3_ticks <= 0L)
                switch (relay_3_state) {
                case RELAY_WAIT:
                    relay_3_state = RELAY_ON;
                    relay_3_ticks += RELAY_3_ON;
                    printf("Activate relay 3.\n");
                    break;
                case RELAY_ON:
                    relay_3_state = RELAY_OFF;
                    relay_3_ticks += RELAY_3_OFF;
                    printf("Deactivate relay 3.\n");
                    break;
                default:
                    relay_3_state = RELAY_WAIT;
                    relay_3_ticks += RELAY_3_WAIT;
                }
    
            /* Now, determine how long to sleep. */
            sleepticks = lcd_ticks;
            if (relay_1_ticks < sleepticks)
                sleepticks = relay_1_ticks;
            if (relay_2_ticks < sleepticks)
                sleepticks = relay_2_ticks;
            if (relay_3_ticks < sleepticks)
                sleepticks = relay_3_ticks;
    
            printf("Sleep for %.1f seconds.\n", (double)sleepticks / 10.0);
    
        }
    
        /* Never reached; press Ctrl+C to break. */
        return 0;
    }
    Note that I deliberately didn't use arrays, as this is microcontroller code, and I wanted to make it as easy as possible to add per-relay quirks later on.

    Using while instead of if allows you to use zero duration for some events. For example, the third latch has zero "off" duration, i.e. it turns off exactly at the start of a new period.

    At 0.1 second precision, 32-bit signed ints can hold a period of over six years. Note that the ticks variables do need to be signed, in case you occasionally sleep maybe one or few ticks longer than intended. (If you just zero the ticks variables, you'll induce clock drift between the latches/LCD.)

    Instead of compile-time constants, you can use variables to hold the durations. If you have an UI for the device (say, a small LCD display with three to five buttons), you could let the user reprogram the intervals (saving to spare variables), then copy all the states at once to the ticks and states variables ("update now?"), to resync or start a new period. State durations can be modified at any time, but they won't take effect until the next time that state is entered into.

    Questions?

  9. #9
    Registered User
    Join Date
    Jun 2011
    Posts
    4,508
    I strongly favor Salem's second suggestion in post #2. This will not only follow the recommended "one interrupt per microcontroller" rule, but will create a neat, time-driven architecture that seems perfectly suited for your application.

    In fact, this is the crux of the book "Embedded C" by Michael Pont, which I'd recommend as a read.

    i do have a concern in that my hardware is outputting to lcd every second, the short time cycles will not allow the per sec update of the lcd, so i need something a bit more refined i fear,,
    This is a non-issue with a time-driven architecture.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Assistance to program a compression algorithm
    By SoraK05 in forum Tech Board
    Replies: 2
    Last Post: 06-16-2014, 06:33 AM
  2. Assistance to program a compression algorithm
    By SoraK05 in forum C++ Programming
    Replies: 0
    Last Post: 06-15-2014, 02:11 PM
  3. Timing in C
    By nicolas in forum C Programming
    Replies: 3
    Last Post: 05-16-2004, 12:52 PM
  4. Timing in C
    By Konrad in forum C Programming
    Replies: 4
    Last Post: 06-24-2003, 01:51 AM
  5. Timing in C/C++
    By csharpdotcom in forum C++ Programming
    Replies: 5
    Last Post: 11-14-2001, 05:18 PM

Tags for this Thread