/
tone.cpp
204 lines (169 loc) · 6.99 KB
/
tone.cpp
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
///////////////////////////////////////////////////////////////////////
//
// tone(pin,frequency[,duration]) generate a tone on a given pin
//
// noTone(pin) switch off the tone on the pin
//
// setToneTimerChannel(timer,channel) force use of given timer/channel
//
///////////////////////////////////////////////////////////////////////
#include "Arduino.h"
#include <HardwareTimer.h>
#define PinTimer(pin) (PIN_MAP[pin].timer_device->clk_id-RCC_TIMER1+1)
#define PinChannel(pin) (PIN_MAP[pin].timer_channel)
// if USE_PIN_TIMER is set, the PWM timer/channel is used for PWM pins
#define USE_PIN_TIMER
// if USE_BSRR is set the tone pin will be written via the fast BSRR register
// instead of using the slow digitalWrite() function in the interrupt handler
#define USE_BSRR
// construct static timer array (
#ifdef STM32_HIGH_DENSITY
// define default timer and channel
#ifndef TONE_TIMER
#define TONE_TIMER 8
#endif
#ifndef TONE_CHANNEL
#define TONE_CHANNEL 8
#endif
HardwareTimer TTimer1(1), TTimer2(2), TTimer3(3), TTimer4(4),TTimer5(5), TTimer6(6), TTimer7(7), TTimer8(8);
HardwareTimer *TTimer[8] = { &TTimer1,&TTimer2,&TTimer3,&TTimer4,&TTimer5,&TTimer6,&TTimer7,&TTimer8 };
#else
// define default timer and channel
#ifndef TONE_TIMER
#define TONE_TIMER 4
#endif
#ifndef TONE_CHANNEL
#define TONE_CHANNEL 4
#endif
HardwareTimer TTimer1(1), TTimer2(2), TTimer3(3), TTimer4(4);
HardwareTimer *TTimer[4] = { &TTimer1,&TTimer2,&TTimer3,&TTimer4 };
#endif
uint8_t tone_force_channel = 0; // forced timer channel
uint8_t tone_force_ntimer = 0; // forced timer
HardwareTimer *tone_timer;// = TTimer[TONE_TIMER-1]; // timer used to generate frequency
uint8_t tone_channel = TONE_CHANNEL; // timer channel used to generate frequency
uint8_t tone_ntimer = TONE_TIMER; // timer used to generate frequency
bool tone_state = true; // last pin state for toggling
short tone_pin = -1; // pin for outputting sound
short tone_freq = 444; // tone frequency (0=pause)
volatile uint32_t tone_nhw = 0; // tone duration in number of half waves
uint16_t tone_tcount = 0; // time between handler calls in 1/36 usec
uint16_t tone_ncount = 0; // handler call between toggling
uint16_t tone_n = 0; // remaining handler calls before toggling
uint32_t tone_next = 0; // counter value of next interrupt
#ifdef USE_BSRR
volatile uint32_t *tone_bsrr; // BSRR set register (lower 16 bits)
uint32_t tone_smask=0; // BSRR set bitmask
uint32_t tone_rmask=0; // BSRR reset bitmask
#endif
////////////////////////////////////////////////////////////////////////////////
// timer hander for tone with no duration specified,
// will keep going until noTone() is called
void tone_handler_1(void) {
tone_next += tone_tcount; // comparator value for next interrupt
tone_timer->setCompare(tone_channel, tone_next); // and install it
if(--tone_n == 0){
tone_state = !tone_state; // toggle tone output
#ifdef USE_BSRR
if(tone_state)
*tone_bsrr = tone_smask;
else
*tone_bsrr = tone_rmask;
#else
digitalWrite(tone_pin,tone_state);// and output it
#endif
tone_n = tone_ncount; // reset interrupt counter
}
}
////////////////////////////////////////////////////////////////////////////////
// timer hander for tone with a specified duration,
// will stop automatically when duration time is up.
void tone_handler_2(void) {
tone_next += tone_tcount;
tone_timer->setCompare(tone_channel, tone_next);
if(--tone_n == 0){
if(tone_freq>0){ // toggle pin
tone_state = !tone_state;
#ifdef USE_BSRR
if(tone_state)
*tone_bsrr = tone_smask;
else
*tone_bsrr = tone_rmask;
#else
digitalWrite(tone_pin,tone_state);// and output it
#endif
}
tone_n = tone_ncount;
if(!--tone_nhw){ // check if tone duration has finished
tone_timer->pause(); // disable timer
pinMode(tone_pin, INPUT); // disable tone pin
}
}
}
////////////////////////////////////////////////////////////////////////////////
// play a tone on given pin with given frequency and optional duration in msec
void tone(uint32_t pin, uint32_t freq, uint32_t duration) {
tone_pin = pin;
#ifdef USE_PIN_TIMER
// if the pin has a PWM timer/channel, use it (unless the timer/channel are forced)
if(PinChannel(tone_pin) && !tone_force_channel){
tone_channel = PinChannel(tone_pin);
tone_ntimer = PinTimer(tone_pin);
} else
#endif
{
// set timer and channel to default resp values forced with setToneTimerChannel
tone_ntimer = tone_force_channel?tone_force_ntimer:TONE_TIMER;
tone_channel = tone_force_channel?tone_force_channel:TONE_CHANNEL;
}
tone_timer = TTimer[tone_ntimer-1];
tone_freq = freq;
tone_nhw = 0;
tone_next = 0;
tone_timer->pause();
if(freq > 0){
uint32_t count = (F_CPU/4)/freq; // timer counts per half wave
tone_ncount = tone_n = (count>>16)+1; // number of 16-bit count chunk
tone_tcount = count/tone_ncount; // size of count chunk
if(duration > 0) // number of half waves to be generated
tone_nhw = ((duration*freq)/1000)<<1;
else // no duration specified, continuous sound until noTone() called
tone_nhw = 0;
pinMode(tone_pin, PWM); // configure output pin
pinMode(tone_pin, OUTPUT); // configure output pin
#ifdef USE_BSRR
// Set up BSRR register values for fast ISR
tone_bsrr = &((PIN_MAP[tone_pin].gpio_device)->regs->BSRR);
tone_smask = (BIT(PIN_MAP[tone_pin].gpio_bit));
tone_rmask = tone_smask<<16;
#endif
// Set up an interrupt on given timer and channel
tone_next = tone_tcount; // prepare channel compare register
tone_timer->setMode(tone_channel,TIMER_OUTPUT_COMPARE);
tone_timer->setCompare(tone_channel,tone_next);
// attach corresponding handler routine
tone_timer->attachInterrupt(tone_channel,tone_nhw?tone_handler_2:tone_handler_1);
// Refresh the tone timer
tone_timer->refresh();
// Start the timer counting
tone_timer->resume();
} else {
// detach handler routine
tone_timer->detachInterrupt(tone_channel);
// disactive pin by configuring it as input
pinMode(tone_pin, INPUT);
}
while(tone_nhw) ; // blocks till duration elapsed
}
////////////////////////////////////////////////////////////////////////////////
// disable tone on specified pin, if any
void noTone(uint32_t pin){
tone(pin,0,0); // it's all handled in tone()
}
////////////////////////////////////////////////////////////////////////////////
// set timer and channel to some different value
// must be called before calling tone() or after noTone() was called
void setToneTimerChannel(uint8_t ntimer, uint8_t channel){
tone_force_ntimer = ntimer;
tone_force_channel = channel;
}