|
| 1 | +/* Tone.cpp |
| 2 | +
|
| 3 | + A Tone Generator Library |
| 4 | +
|
| 5 | + Written by Brett Hagman |
| 6 | +
|
| 7 | + This library is free software; you can redistribute it and/or |
| 8 | + modify it under the terms of the GNU Lesser General Public |
| 9 | + License as published by the Free Software Foundation; either |
| 10 | + version 2.1 of the License, or (at your option) any later version. |
| 11 | +
|
| 12 | + This library is distributed in the hope that it will be useful, |
| 13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | + Lesser General Public License for more details. |
| 16 | +
|
| 17 | + You should have received a copy of the GNU Lesser General Public |
| 18 | + License along with this library; if not, write to the Free Software |
| 19 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | +
|
| 21 | +Version Modified By Date Comments |
| 22 | +------- ----------- -------- -------- |
| 23 | +0001 B Hagman 09/08/02 Initial coding |
| 24 | +0002 B Hagman 09/08/18 Multiple pins |
| 25 | +0003 B Hagman 09/08/18 Moved initialization from constructor to begin() |
| 26 | +0004 B Hagman 09/09/26 Fixed problems with ATmega8 |
| 27 | +0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers |
| 28 | + 09/11/25 Changed pin toggle method to XOR |
| 29 | + 09/11/25 Fixed timer0 from being excluded |
| 30 | +0006 D Mellis 09/12/29 Replaced objects with functions |
| 31 | +0007 M Sproul 10/08/29 Changed #ifdefs from cpu to register |
| 32 | +0008 S Kanemoto 12/06/22 Fixed for Leonardo by @maris_HY |
| 33 | +0009 Arduino.org 15/06/30 Add M0/M0 Pro support |
| 34 | +0010 Arduino.org 16/07/27 Added Arduino Primo support |
| 35 | +*************************************************/ |
| 36 | + |
| 37 | + |
| 38 | +#include "Tone.h" |
| 39 | +#include "WVariant.h" |
| 40 | + |
| 41 | +unsigned long int count_duration=0; |
| 42 | +volatile bool no_stop = false; |
| 43 | +uint8_t pin_sound=0; |
| 44 | + |
| 45 | + |
| 46 | +void tone(uint8_t pin, unsigned int frequency, unsigned long duration) |
| 47 | +{ |
| 48 | + unsigned int time_per=0; |
| 49 | + |
| 50 | + if((frequency < 20) | (frequency > 25000)) return; |
| 51 | + |
| 52 | + |
| 53 | + float per=float(1)/frequency; |
| 54 | + time_per=per/0.000008; |
| 55 | + unsigned int duty=time_per/2; |
| 56 | + if(duration > 0){ |
| 57 | + no_stop = false; |
| 58 | + float mil=float(duration)/1000; |
| 59 | + if(per>mil) |
| 60 | + count_duration=1; |
| 61 | + else |
| 62 | + count_duration= mil/per; |
| 63 | + } |
| 64 | + else |
| 65 | + no_stop = true; |
| 66 | + |
| 67 | + // Configure PWM |
| 68 | + static uint16_t seq_values[]={0}; |
| 69 | + //In each value, the most significant bit (15) determines the polarity of the output |
| 70 | + //0x8000 is MSB = 1 |
| 71 | + seq_values[0]= duty | 0x8000; |
| 72 | + nrf_pwm_sequence_t const seq={ |
| 73 | + seq_values, |
| 74 | + NRF_PWM_VALUES_LENGTH(seq_values), |
| 75 | + 0, |
| 76 | + 0 |
| 77 | + }; |
| 78 | + |
| 79 | +#if 0 |
| 80 | + //assign pin to pwm channel - look at WVariant.h for details about ulPWMChannel attribute |
| 81 | + uint8_t pwm_type=g_APinDescription[pin].ulPWMChannel; |
| 82 | + if(pwm_type == NOT_ON_PWM) |
| 83 | + return; |
| 84 | + |
| 85 | + uint32_t pins[NRF_PWM_CHANNEL_COUNT]={NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}; |
| 86 | + pins[pwm_type & 0x0F]=g_APinDescription[pin].ulPin; |
| 87 | + IRQn_Type IntNo = PWM0_IRQn; |
| 88 | + NRF_PWM_Type * PWMInstance = NRF_PWM0; |
| 89 | + switch(pwm_type &0xF0){ |
| 90 | + case 16://0x10 |
| 91 | + PWMInstance = NRF_PWM1; |
| 92 | + IntNo = PWM1_IRQn; |
| 93 | + break; |
| 94 | + case 32://0x20 |
| 95 | + PWMInstance = NRF_PWM2; |
| 96 | + IntNo = PWM2_IRQn; |
| 97 | + break; |
| 98 | + } |
| 99 | +#else |
| 100 | + // Use fixed PWM2, TODO could conflict with other usage |
| 101 | + uint32_t pins[NRF_PWM_CHANNEL_COUNT]={NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}; |
| 102 | + pins[0] = g_ADigitalPinMap[pin]; |
| 103 | + |
| 104 | + IRQn_Type IntNo = PWM2_IRQn; |
| 105 | + NRF_PWM_Type * PWMInstance = NRF_PWM2; |
| 106 | +#endif |
| 107 | + |
| 108 | + nrf_pwm_pins_set(PWMInstance, pins); |
| 109 | + nrf_pwm_enable(PWMInstance); |
| 110 | + nrf_pwm_configure(PWMInstance, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, time_per); |
| 111 | + nrf_pwm_decoder_set(PWMInstance, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO); |
| 112 | + nrf_pwm_sequence_set(PWMInstance, 0, &seq); |
| 113 | + nrf_pwm_shorts_enable(PWMInstance, NRF_PWM_SHORT_SEQEND0_STOP_MASK); |
| 114 | + |
| 115 | + // enable interrupt |
| 116 | + nrf_pwm_event_clear(PWMInstance, NRF_PWM_EVENT_PWMPERIODEND); |
| 117 | + nrf_pwm_int_enable(PWMInstance, NRF_PWM_INT_PWMPERIODEND_MASK); |
| 118 | + NVIC_SetPriority(IntNo, 6); //low priority |
| 119 | + NVIC_ClearPendingIRQ(IntNo); |
| 120 | + NVIC_EnableIRQ(IntNo); |
| 121 | +} |
| 122 | + |
| 123 | + |
| 124 | +void noTone(uint8_t pin) |
| 125 | +{ |
| 126 | +#if 0 |
| 127 | + uint8_t pwm_type=g_APinDescription[pin].ulPWMChannel; |
| 128 | + NRF_PWM_Type * PWMInstance = NRF_PWM0; |
| 129 | + switch(pwm_type &0xF0){ |
| 130 | + case 16://0x10 |
| 131 | + PWMInstance = NRF_PWM1; |
| 132 | + break; |
| 133 | + case 32://0x20 |
| 134 | + PWMInstance = NRF_PWM2; |
| 135 | + break; |
| 136 | + } |
| 137 | +#else |
| 138 | + NRF_PWM_Type * PWMInstance = NRF_PWM2; |
| 139 | +#endif |
| 140 | + |
| 141 | + nrf_pwm_task_trigger(PWMInstance, NRF_PWM_TASK_STOP); |
| 142 | + nrf_pwm_disable(PWMInstance); |
| 143 | +} |
| 144 | + |
| 145 | +#ifdef __cplusplus |
| 146 | +extern "C"{ |
| 147 | +#endif |
| 148 | + |
| 149 | +#if 0 |
| 150 | +void PWM0_IRQHandler(void){ |
| 151 | + nrf_pwm_event_clear(NRF_PWM0, NRF_PWM_EVENT_PWMPERIODEND); |
| 152 | + if(!no_stop){ |
| 153 | + count_duration--; |
| 154 | + if(count_duration == 0) |
| 155 | + noTone(pin_sound); |
| 156 | + else |
| 157 | + nrf_pwm_task_trigger(NRF_PWM0, NRF_PWM_TASK_SEQSTART0); |
| 158 | + } |
| 159 | + else |
| 160 | + nrf_pwm_task_trigger(NRF_PWM0, NRF_PWM_TASK_SEQSTART0); |
| 161 | +} |
| 162 | + |
| 163 | +void PWM1_IRQHandler(void){ |
| 164 | + nrf_pwm_event_clear(NRF_PWM1, NRF_PWM_EVENT_PWMPERIODEND); |
| 165 | + if(!no_stop){ |
| 166 | + count_duration--; |
| 167 | + if(count_duration == 0) |
| 168 | + noTone(pin_sound); |
| 169 | + else |
| 170 | + nrf_pwm_task_trigger(NRF_PWM1, NRF_PWM_TASK_SEQSTART0); |
| 171 | + } |
| 172 | + else |
| 173 | + nrf_pwm_task_trigger(NRF_PWM1, NRF_PWM_TASK_SEQSTART0); |
| 174 | +} |
| 175 | +#endif |
| 176 | + |
| 177 | +void PWM2_IRQHandler(void){ |
| 178 | + nrf_pwm_event_clear(NRF_PWM2, NRF_PWM_EVENT_PWMPERIODEND); |
| 179 | + if(!no_stop){ |
| 180 | + count_duration--; |
| 181 | + if(count_duration == 0) |
| 182 | + noTone(pin_sound); |
| 183 | + else |
| 184 | + nrf_pwm_task_trigger(NRF_PWM2, NRF_PWM_TASK_SEQSTART0); |
| 185 | + } |
| 186 | + else |
| 187 | + nrf_pwm_task_trigger(NRF_PWM2, NRF_PWM_TASK_SEQSTART0); |
| 188 | +} |
| 189 | + |
| 190 | +#ifdef __cplusplus |
| 191 | +} |
| 192 | +#endif |
0 commit comments