Generating Ticks from an additional clock crystal

Date:2017-08-07

Intro

Our favorite microcontroller features two pins to use an external clock crystal at 32768 Hz to drive timer/counter2. We use its overflows to generate ticks, moreover we could put the microcontroller to sleep between these events, if needed.

Design Decisions

  • Timer/counter2 is driven asynchronouosly at 32768 Hz

  • Timer/counter2 is an 8-bit counter, counting 256 cycles unless instructed otherwise

  • So we get 128 ticks/second:

    32768/256 = 128
    
  • prescaler offers dividers of 8, 32, 64, 128, 256, 1024; with these values we could also generate 16, 4, 2 , 1, 1/2, 1/4 ticks/second.

  • in addition we could set CTC mode (clear counter on compare match) and choose the compare value. So for example setting the prescaler to 8 and the compare match value to 128-1 we should get 32 ticks/second again.

Setting up Timer2 for asynchronous operation

We set up timer/counter2 in normal mode, it will count 256 clock cycles and then issue an overflow interrupt. Then we switch the clock source to the external clock crystal via asynchronuos mode.

: +ticks
  ...
  [ %00000000                 \ normal mode
  ] literal TCCR2A c!
  [ %00000001                 \ 001 = clock_ts2, prescaler 1
  ] literal TCCR2B c!
  ASSR_AS2 ASSR  c!           \ clock source: 32 kiHz crystal
  ...
;

TODO: add toggling an output pin for debug!

Serving Timer2 overflow interrupts

Similar to the code explained in the section Generating Ticks from the main crystal, the ISR increments variable ct.ticks. The main loop will acknowledge the effect by incrementing ct.ticks.follow.

\ timer overflow ISR
: tick2_isr ( -- )  1 ct.ticks +! ;

: +ticks    ( -- )
  ...
  ['] tick2_isr TIMER2_OVFAddr int!
  TIMSK2 c@ $01 or TIMSK2 c!  \  enable OVF2 interupt
;

variable ct.ticks
variable ct.ticks.follow

: tick.over? ( -- t/f ) ct.ticks.follow @  ct.ticks @  - 0< ;
: tick.over! ( -- )     1 ct.ticks.follow +! ;

Putting it all together

We should find the above code snippets used in the main program somehow like this:

include ewlib/clockticks_clock_crystal.fs

variable ticks
: init
  ...
  0 ticks !
  +ticks
;

: run-loop
  init
  begin
    tick.over? if
      tick.over!
      \ one tick over, do someting
      1 ticks +!    \ count ticks
    then

    second.over? > if
      ticks @ ticks/sec - ticks !
      \ one second over, do something!
      ...
    then

  again
;

The Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
\ 2017-03-27 EW   ewlib/clock_tick2_clockcrystal.fs
\
\ Written in 2017 by Erich Wälde <erich.waelde@forth-ev.de>
\
\ To the extent possible under law, the author(s) have dedicated
\ all copyright and related and neighboring rights to this software
\ to the public domain worldwide. This software is distributed
\ without any warranty.
\
\ You should have received a copy of the CC0 Public Domain
\ Dedication along with this software. If not, see
\ <http://creativecommons.org/publicdomain/zero/1.0/>.
\

#128 constant ticks/sec
variable ct.ticks
variable ct.ticks.follow
variable last.tick[6]
variable last.tick[7]


\ timer 2 overflow interrupt service routine
: tick2_isr
  1 ct.ticks +!
;

: tick.over? ( -- t/f ) ct.ticks.follow @  ct.ticks @  - 0< ;
: tick.over! ( -- )     1 ct.ticks.follow +! ;


\ enable ticks
\ crystal:   32768 /sec
\ clock src: 32768 /sec
\ overflow:  32768/256 = 128 /sec
: +ticks
  0 ct.ticks         !
  0 ct.ticks.follow  !
  0 last.tick[6]     !
  0 last.tick[7]     !

  \ --- timer2 ---
  [ %00000000                   \ normal mode
  ] literal TCCR2A c!
  [ %00000001                   \ 001 = clock_ts2
  ] literal TCCR2B c!
  ASSR_AS2 ASSR  c!             \ source: 32 kiHz crystal
  ['] tick2_isr TIMER2_OVFAddr int! \ register ISR
  TIMSK2 c@ $01 or TIMSK2 c!    \  enable OVF2 interupt
;

\ disable ticks
: -ticks
  TIMSK2 c@
  [ $01 invert ] literal
  and TIMSK2  c! ( clr Timer 2 )
  $00  ASSR   c!
  $00  TCCR2B c!
  $07  TIFR2  c! \ clear interrupt flags, jibc
;


\ one second == 128 ticks
\ half second == 64 ticks
\ that is a toggle on bit 6 of ct.ticks.follow
: half.second.over? ( -- 0|1|2 )
  \ return: 0 == false
  \         1 == half second over
  \         2 == second over
  ct.ticks.follow c@
  $0040 and 0= 0=  \ extract significant bit as t/f
  dup last.tick[6] @ = if
    \ no change, done
    drop 0
  else
    dup 0= if
      \ falling edge, second over
      2
    else
      \ rising edge, half second over
      1
    then
    swap
    ( sig.bit-t/f ) last.tick[6] !
  then
;

: second.over? ( -- t/f )
  ct.ticks.follow c@  $0080 and 0= 0=
  dup  last.tick[7] @  = if
    drop 0
  else
    last.tick[7] !
    -1
  then
;