mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-05 09:38:10 +00:00
205 lines
7.0 KiB
C++
205 lines
7.0 KiB
C++
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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;
|
|
}
|