Periodic Jobs

Date:

2017-08-09

Intro

With the previous sections we do have clock ticks, we do have the tools to keep track of time, so now what? We need a means to update the display periodically or do other fancy stuff, perhaps periodically too? So this section will show one way to call functions, every time a second, a minute, etc. is over.

Design Decisions

  • There will be a list of functions called jobs

  • There will be a section of code in the run-loop function, which inspects the overflow flags set in timeup and calls the above jobs and clears their flags

  • After each call of a job the main loop is run again to keep track of ticks and their duties. So this delays jobs for higher counters a little but not by much, but it calls job.ticks sooner again

Jobs

The jobs are regular functions which refrain from changing the stack. They are defined, their XTs are stored in a flash table.

: job.tick   ;
: job.sec    ( count uptime? toggle LED? update display? ) ;
: job.min    ( update display? ) ;
: job.hour   ( hit the gong? ) ;
: job.day    ;
: job.month  year @  month @ 1+  tu.upd.limits ;
: job.year   ;

create Jobs
  ' job.tick ,
  ' job.sec , ' job.min ,   ' job.hour ,
  ' job.day , ' job.month , ' job.year ,

In order to call them in a loop, an index variable jobCount is used. It is advanced from 1 (job.sec) to 6 every round through the main loop.

variable jobCount
: jobCount++ ( -- )  jobCount @  #6 < if  1 jobCount +!  then ;

TODO: give the magic #6 a name?

Measuring Uptime

A simple application of the whole idea is to count the uptime. A 2variable (32 bit) is defined. It is cleared at startup and incremented every second.

2variable uptime
: .uptime   ( -- )  uptime 2@  decimal ud. [char] s emit   ;
: ++uptime  ( -- )  1.  uptime 2@  d+  uptime 2! ;

: init
  ...
  0. uptime  2!
;

: job.sec   ( -- )   ++uptime ;

The uptime can be displayed with .uptime, however, either after exiting the main loop, or in the same or another job, or on the serial prompt after adding the multitasker.

After some time a more elaborate version .uptime.dhms looks better (# u0.r is used, because . places a space after the last digit):

: .uptime.dhms  ( -- )
  uptime 2@
  #60 ud/mod  #60 ud/mod  #24 ud/mod
  drop
  #3 u0.r [char] d emit space
  #2 u0.r [char] : emit
  #2 u0.r [char] : emit
  #2 u0.r
;

Putting it all together

All jobs and their handling is defined in the main program file. Checking the flags and calling the jobs needs to be done in run-loop.

\ main-....fs
include ewlib/clockticks_clock_crystal.fs
include ewlib/timeup_v1.fs
include ewlib/leap_year_q.fs

\ --- uptime
2variable uptime
: .uptime   ( -- )  uptime 2@  decimal ud. [char] s emit   ;
: ++uptime  ( -- )  1.  uptime 2@  d+  uptime 2! ;

\ --- timeup jobs ---------------------------
: job.tick  ;
: job.sec
  ++uptime
;
: job.min
  \ update display?
;
: job.hour
  \ hit the gong?
;
: job.day   ;
: job.month
  year @  month @ 1+  tu.upd.limits
;
: job.year  ;

create Jobs
  ' job.tick ,
  ' job.sec , ' job.min ,   ' job.hour ,
  ' job.day , ' job.month , ' job.year ,

variable jobCount
: jobCount++
  jobCount @
  #6 < if
    1 jobCount +!
  then
;

variable ticks
: init
  ...
  0  ticks    !
  #6 jobCount !
  0. uptime  2!
  timeup.init
  +ticks
;

: sec.over? ( -- t/f)  ticks @ 1+  ticks/sec > ;

: run-loop
  init
  begin
    tick.over? if
      tick.over!                     \ acknowledge
      1 ticks +!                     \ increment ticks
      job.tick                       \ do something
    then

    sec.over? if
      ticks @ ticks/sec - ticks !    \ reduce ticks
      timeup                         \ advance clock counters
      1 jobCount !                   \ start jobs
    then


    jobCount @ bv tu.flags fset? if  \ run one job per loop
      jobCount @
      dup Jobs + @i execute
      bv tu.flags fclr
    then
    jobCount++

  again
;

This code is and looks very old, to my eyes it could use a little refresh, /me thinks. On the other hand, it works :-)

The Code

 1\ 2015-10-11 ewlib/timeup_v0.0.fs
 2\
 3\ Written in 2015 by Erich Wälde <erich.waelde@forth-ev.de>
 4\
 5\ To the extent possible under law, the author(s) have dedicated
 6\ all copyright and related and neighboring rights to this software
 7\ to the public domain worldwide. This software is distributed
 8\ without any warranty.
 9\
10\ You should have received a copy of the CC0 Public Domain
11\ Dedication along with this software. If not, see
12\ <http://creativecommons.org/publicdomain/zero/1.0/>.
13\
14\
15\ variables
16\     tu.counts -- fields available as:
17\     tick sec min hour day month year
18\ words:
19\     timeup.init
20\     timeup
21\     lastday_of_month ( year month -- last_day )
22\     tu.get  ( -- S M H d m Y )
23\     tu.set  ( Y m d H M S -- )
24\     tu.show ( -- )
25
26#include leap_year_q.fs
27
28variable tu.flags
29
30variable tu.counts     #7 cells allot
31tu.counts            constant tick
32tu.counts #1 cells + constant sec
33tu.counts #2 cells + constant min
34tu.counts #3 cells + constant hour
35tu.counts #4 cells + constant day
36tu.counts #5 cells + constant month
37tu.counts #6 cells + constant year
38
39variable tu.limits     #6       allot
40
41create tu.lastday_of_month
42   #31 , #28 , #31 , #30 , #31 , #30 ,
43   #31 , #31 , #30 , #31 , #30 , #31 ,
44
45: lastday_of_month ( year month -- last_day )
46  dup 1-                \ array starts at 0
47  tu.lastday_of_month + @i
48  swap #2 = if          \ if month == 2
49    swap leap_year? if  \   if leap_year
50      1+                \     month += 1
51    then
52  else                  \ else
53    swap drop           \   remove year
54  then
55;
56
57: timeup.init
58  0      tu.flags !
59  tu.counts #8 erase
60  #60    tu.limits 1 + c!
61  #60    tu.limits 2 + c!
62  #24    tu.limits 3 + c!
63  #31    tu.limits 4 + c! \ fixme: may be wrong later!
64  #12    tu.limits 5 + c!
65;
66
67: timeup ( -- )
68  $02 tu.flags fset                     \ secflag++
69  1 sec +!                              \ sec++
70
71  \ for ( sec ) min hour day month year
72  #6 1 do
73    i cells tu.counts + @   1+          \ Counts[i]+1
74    i       tu.limits + c@              \ Limits[i]
75    > if                                \ if C[i]+1 > L[i]
76      0  i cells tu.counts +  !         \ . C[i]=0
77      i 1+ bv tu.flags fset             \ . F[i+1]++
78      1 i 1+ cells tu.counts + +!       \ . C[i+1]++
79    then                                \ fi
80  loop
81;
82
83\ update lastday_of_month in tu.limits
84\ once current date is known
85: tu.upd.limits ( Y m -- )
86  ( Y m ) lastday_of_month  tu.limits #4 + c!
87;