|
| 1 | +/////////////////////////////////////////////////////////////////////// |
| 2 | +// |
| 3 | +// tone(pin,frequency[,duration]) generate a tone on a given pin |
| 4 | +// |
| 5 | +// noTone(pin) switch off the tone on the pin |
| 6 | +// |
| 7 | +// setToneTimerChannel(timer,channel) force use of given timer/channel |
| 8 | +// |
| 9 | +/////////////////////////////////////////////////////////////////////// |
| 10 | + |
| 11 | +#include "Arduino.h" |
| 12 | +#include <HardwareTimer.h> |
| 13 | + |
| 14 | +// define default timer and channel |
| 15 | +#ifndef TONE_TIMER |
| 16 | +#define TONE_TIMER 4 |
| 17 | +#endif |
| 18 | +#ifndef TONE_CHANNEL |
| 19 | +#define TONE_CHANNEL 4 |
| 20 | +#endif |
| 21 | + |
| 22 | +#define PinTimer(pin) (PIN_MAP[pin].timer_device->clk_id-RCC_TIMER1+1) |
| 23 | +#define PinChannel(pin) (PIN_MAP[pin].timer_channel) |
| 24 | + |
| 25 | +// if USE_PIN_TIMER is set, the PWM timer/channel is used for PWM pins |
| 26 | +#define USE_PIN_TIMER |
| 27 | + |
| 28 | +// if USE_BSRR is set the tone pin will be written via the fast BSRR register |
| 29 | +// instead of using the slow digitalWrite() function in the interrupt handler |
| 30 | +#define USE_BSRR |
| 31 | + |
| 32 | +// construct static timer array ( |
| 33 | +HardwareTimer TTimer1(1), TTimer2(2), TTimer3(3), TTimer4(4); |
| 34 | +#ifdef STM32_HIGH_DENSITY |
| 35 | +HardwareTimer TTimer5(5), TTimer6(6), TTimer7(7), TTimer8(8); |
| 36 | +#endif |
| 37 | +HardwareTimer *TTimer[4] { |
| 38 | + &TTimer1,&TTimer2,&TTimer3,&TTimer4 |
| 39 | +#ifdef STM32_HIGH_DENSITY |
| 40 | + ,&TTimer5,&TTimer6,&TTimer7,&TTimer8 |
| 41 | +#endif |
| 42 | + }; |
| 43 | + |
| 44 | +uint8_t tone_force_channel = 0; // forced timer channel |
| 45 | +uint8_t tone_force_ntimer = 0; // forced timer |
| 46 | + |
| 47 | +HardwareTimer *tone_timer = TTimer[TONE_TIMER]; // timer used to generate frequency |
| 48 | +uint8_t tone_channel = TONE_CHANNEL; // timer channel used to generate frequency |
| 49 | +uint8_t tone_ntimer = TONE_TIMER; // timer used to generate frequency |
| 50 | + |
| 51 | +bool tone_state = true; // last pin state for toggling |
| 52 | +short tone_pin = -1; // pin for outputting sound |
| 53 | +short tone_freq = 444; // tone frequency (0=pause) |
| 54 | +uint32_t tone_nhw = 0; // tone duration in number of half waves |
| 55 | +uint16_t tone_tcount = 0; // time between handler calls in 1/36 usec |
| 56 | +uint16_t tone_ncount = 0; // handler call between toggling |
| 57 | +uint16_t tone_n = 0; // remaining handler calls before toggling |
| 58 | +uint32_t tone_next = 0; // counter value of next interrupt |
| 59 | + |
| 60 | +#ifdef USE_BSRR |
| 61 | +volatile uint32_t *tone_bsrr; // BSRR set register (lower 16 bits) |
| 62 | +uint32_t tone_smask=0; // BSRR set bitmask |
| 63 | +uint32_t tone_rmask=0; // BSRR reset bitmask |
| 64 | +#endif |
| 65 | + |
| 66 | + |
| 67 | +//////////////////////////////////////////////////////////////////////////////// |
| 68 | +// timer hander for tone with no duration specified, |
| 69 | +// will keep going until noTone() is called |
| 70 | +void tone_handler_1(void) { |
| 71 | + tone_next += tone_tcount; // comparator value for next interrupt |
| 72 | + tone_timer->setCompare(tone_channel, tone_next); // and install it |
| 73 | + if(--tone_n == 0){ |
| 74 | + tone_state = !tone_state; // toggle tone output |
| 75 | + |
| 76 | +#ifdef USE_BSRR |
| 77 | + if(tone_state) |
| 78 | + *tone_bsrr = tone_smask; |
| 79 | + else |
| 80 | + *tone_bsrr = tone_rmask; |
| 81 | +#else |
| 82 | + digitalWrite(tone_pin,tone_state);// and output it |
| 83 | +#endif |
| 84 | + |
| 85 | + tone_n = tone_ncount; // reset interrupt counter |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +//////////////////////////////////////////////////////////////////////////////// |
| 90 | +// timer hander for tone with a specified duration, |
| 91 | +// will stop automatically when duration time is up. |
| 92 | +void tone_handler_2(void) { |
| 93 | + tone_next += tone_tcount; |
| 94 | + tone_timer->setCompare(tone_channel, tone_next); |
| 95 | + if(--tone_n == 0){ |
| 96 | + if(tone_freq>0){ // toggle pin |
| 97 | + tone_state = !tone_state; |
| 98 | +#ifdef USE_BSRR |
| 99 | + if(tone_state) |
| 100 | + *tone_bsrr = tone_smask; |
| 101 | + else |
| 102 | + *tone_bsrr = tone_rmask; |
| 103 | +#else |
| 104 | + digitalWrite(tone_pin,tone_state);// and output it |
| 105 | +#endif |
| 106 | + } |
| 107 | + tone_n = tone_ncount; |
| 108 | + if(!--tone_nhw){ // check if tone duration has finished |
| 109 | + tone_timer->pause(); // disable timer |
| 110 | + pinMode(tone_pin, INPUT); // disable tone pin |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +//////////////////////////////////////////////////////////////////////////////// |
| 116 | +// play a tone on given pin with given frequency and optional duration in msec |
| 117 | +void tone(uint32_t pin, uint32_t freq, uint32_t duration) { |
| 118 | + tone_pin = pin; |
| 119 | + |
| 120 | +#ifdef USE_PIN_TIMER |
| 121 | + // if the pin has a PWM timer/channel, use it (unless the timer/channel are forced) |
| 122 | + if(PinChannel(tone_pin) && !tone_force_channel){ |
| 123 | + tone_channel = PinChannel(tone_pin); |
| 124 | + tone_ntimer = PinTimer(tone_pin); |
| 125 | + } else |
| 126 | +#endif |
| 127 | + { |
| 128 | + // set timer and channel to default resp values forced with setToneTimerChannel |
| 129 | + tone_ntimer = tone_force_channel?tone_force_ntimer:TONE_TIMER; |
| 130 | + tone_channel = tone_force_channel?tone_force_channel:TONE_CHANNEL; |
| 131 | + } |
| 132 | + |
| 133 | + tone_timer = TTimer[tone_ntimer-1]; |
| 134 | + tone_freq = freq; |
| 135 | + tone_nhw = 0; |
| 136 | + tone_next = 0; |
| 137 | + |
| 138 | + tone_timer->pause(); |
| 139 | + |
| 140 | + if(freq > 0 || duration >0 ){ |
| 141 | + uint32_t count = 18000000/freq; // timer counts per half wave |
| 142 | + tone_ncount = tone_n = (count>>16)+1; // number of 16-bit count chunk |
| 143 | + tone_tcount = count/tone_ncount; // size of count chunk |
| 144 | + if(duration > 0) // number of half waves to be generated |
| 145 | + tone_nhw = ((duration*(freq>0?freq:100))/1000)<<1; |
| 146 | + else // no duration specified, continuous sound until noTone() called |
| 147 | + tone_nhw = 0; |
| 148 | + |
| 149 | + pinMode(tone_pin, PWM); // configure output pin |
| 150 | + pinMode(tone_pin, OUTPUT); // configure output pin |
| 151 | + |
| 152 | +#ifdef USE_BSRR |
| 153 | + // Set up BSRR register values for fast ISR |
| 154 | + tone_bsrr = &((PIN_MAP[tone_pin].gpio_device)->regs->BSRR); |
| 155 | + tone_smask = (BIT(PIN_MAP[tone_pin].gpio_bit)); |
| 156 | + tone_rmask = tone_smask<<16; |
| 157 | +#endif |
| 158 | + |
| 159 | + // Set up an interrupt on given timer and channel |
| 160 | + tone_next = tone_tcount; // prepare channel compare register |
| 161 | + tone_timer->setMode(tone_channel,TIMER_OUTPUT_COMPARE); |
| 162 | + tone_timer->setCompare(tone_channel,tone_next); |
| 163 | + // attach corresponding handler routine |
| 164 | + tone_timer->attachInterrupt(tone_channel,tone_nhw?tone_handler_2:tone_handler_1); |
| 165 | + |
| 166 | + // Refresh the tone timer |
| 167 | + tone_timer->refresh(); |
| 168 | + |
| 169 | + // Start the timer counting |
| 170 | + tone_timer->resume(); |
| 171 | + |
| 172 | + } else { |
| 173 | + |
| 174 | + // detach handler routine |
| 175 | + tone_timer->detachInterrupt(tone_channel); |
| 176 | + // disactive pin by configuring it as input |
| 177 | + pinMode(tone_pin, INPUT); |
| 178 | + |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +//////////////////////////////////////////////////////////////////////////////// |
| 183 | +// disable tone on specified pin, if any |
| 184 | +void noTone(uint32_t pin){ |
| 185 | + tone(pin,-1); // it's all handled in tone() |
| 186 | +} |
| 187 | + |
| 188 | +//////////////////////////////////////////////////////////////////////////////// |
| 189 | +// set timer and channel to some different value |
| 190 | +// must be called before calling tone() or after noTone() was called |
| 191 | +void setToneTimerChannel(uint8_t ntimer, uint8_t channel){ |
| 192 | + tone_force_ntimer = ntimer; |
| 193 | + tone_force_channel = channel; |
| 194 | +} |
0 commit comments