-
Notifications
You must be signed in to change notification settings - Fork 30
/
heart.mu4
105 lines (85 loc) · 3.48 KB
/
heart.mu4
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
96
97
98
99
100
101
102
103
104
105
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading AVR heartbeat (software PWM)
assembler
| Helpful macro for waking tasks.
: wake ( 'user) asm{ \f dup xl lds xl dec xl sts } ;
| If a task is defined, wake it; otherwise, do nothing.
: ?wake .equates. token' if execute \a wake ^ then 2drop ( token) ;
forth
__meta
hex
| Output to modulate - probably connected to an LED.
| Here is an example; do something similar before loading this file.
| 0 PORTB ( bit port) 2constant heart-led
| We want the heartbeat to be a triangle wave. The simplest way to achieve
| this, with the fewest glitches and corner cases, is to XOR a sawtooth
| wave at frequency f with a polarity square wave at f/2.
| 00 01 02 ... fc fd fe ff 00 01 02 ... fc fd fe ff Sawtooth
| 00 00 00 ... 00 00 00 00 ff ff ff ... ff ff ff ff Polarity
| 00 01 02 ... fc fd fe ff ff fe fd ... 03 02 01 00 Output
1 var heart-beat | how far we are into our 256-point pwm cycle
1 var heart-sawtooth | current sawtooth value
1 var heart-polarity | current polarity value - 00 or ff
1 var heart-output | current output pwm = sawtooth xor polarity
| heart-beat is incremented by the timer overflow interrupt.
|
| heart-sawtooth, heart-polarity, and heart-output are re-computed by the
| timer task every time it is woken by the timer interrupt.
| At 16M clock, timer overflows at 62.5k, or every 16us.
| At 8M clock, timer overflows at 31.25k, or every 32us.
|
| To keep things running at the same rate in both cases, we increment
| heart-beat by 2 when running at 8M.
TIMER0_OVF handler
label timer0-overflow-handler
x pushw SREG xl in xl push ( push X and SREG)
heart-beat xl lds xl xh mov xl inc
.ifndef cksel ( using internal 8M RC clock) xl inc .then
xl xh eor heart-beat xl sts ( xh has bits that have toggled)
xl tst 0= if
( Wake the heart task every 256 ticks - every 4.096 ms.)
?wake heart-user-area
then
4 xh sbrs never ( jump to then unless bit 4 set)
( Wake button tasks every 16 ticks - every 256 us.)
?wake button0-user-area
?wake button1-user-area
then
| If heart-beat u< heart-output, light the LED.
heart-beat xl lds heart-output xh lds xh xl cp ( beat - output)
u< if heart-led cbi ( LED on) else
heart-led sbi ( LED off) then
( Restore SREG and X and exit handler)
xl pop SREG xl out x popw reti ;c
| Consume one wakeup and call yield.
label nap
u y movw cli 0 g0 ldu ( status) g0 inc 0 g0 stu sei yield jmp ;c
label heart-task
| Initialize everything.
heart-led sbi ( LED off)
heart-led DDRB PORTB - + ( convert PORTx to DDRx) sbi ( set pin as output)
01 xl ldi TIMSK0 xl out ( enable TOVR0)
TCCR0B xl out ( set timer0 clock to CPU/1)
00 xl ldi TCCR0A xl out ( set up timer0 - normal mode)
( leave xl set to 00!)
| Zero everything.
heart-beat xl sts
heart-sawtooth xl sts
heart-polarity xl sts
heart-output xl sts
| Enable watchdog.
18 ( WDCE + WDE) h0 ldi
8 ( WDE) 3 ( 16K = 0.125s timeout) + h1 ldi
wdr WDTCSR h0 out WDTCSR h1 out
| The heart of the task. ;-)
begin
wdr nap rcall
heart-polarity xh lds
heart-sawtooth xl lds xl inc heart-sawtooth xl sts
0= if ( sawtooth rolled over)
xh com heart-polarity xh sts ( complement polarity)
then
xh xl eor heart-output xl sts ( new output trigger point)
again ;c