/ Trinket_Question_Block_Sound_Jewelry / Trinket_Question_Block_Sound_Jewelry.ino
Trinket_Question_Block_Sound_Jewelry.ino
1 // SPDX-FileCopyrightText: 2018 Phillip Burgess/paintyourdragon for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 /* ----------------------------------------------------------------------- 6 Super Mario Bros-inspired coin sound for Adafruit Trinket & Gemma. 7 8 Requires piezo speaker between pins 0 & 1, vibration sensor or 9 momentary button between pin 2 & GND. Tap for "bling!" noise. 10 Optional LED+resistor on pin 4 for light during play. 11 12 Runs equally well on a 16 MHz or 8 MHz Trinket, or on Gemma. Use what 13 you've got, no need to get all HOMG MOAR MEGAHURTZ!!1! about it. :) 14 15 This is NOT good beginner code to learn from...there's very little 16 resemblance to a "normal" Arduino sketch as we poke around with ATtiny 17 peripheral registers directly; will NOT run on other Arduino boards. 18 Commented like mad regardless, might discover fun new stuff. 19 20 Written by Phillip Burgess for Adafruit Industries. Public domain. 21 ----------------------------------------------------------------------- */ 22 23 #include <avr/power.h> 24 #include <avr/sleep.h> 25 26 // These variables are declared 'volatile' because their values may change 27 // inside interrupts, independent of the mainline code. This keeps the 28 // optimizer from removing lines it would otherwise regard as unnecessary. 29 // 'quietness' is basically the inverse of volume -- the code was a little 30 // smaller expressing it this way. 0 = max volume, 127 = quietest. 31 // 'count' is incremented while generating a square wave. Used for timing, 32 // and bit 0 indicates whether this is the 'high' or 'low' part of the wave. 33 volatile uint8_t quietness; 34 volatile uint16_t count; 35 36 // ONE-TIME INITIALIZATION ----------------------------------------------- 37 38 void setup() { 39 #if (F_CPU == 16000000L) 40 clock_prescale_set(clock_div_1); 41 #endif 42 43 // ATtiny85 has a special high-speed 64 MHz PLL mode than can be used 44 // as an input to Timer/Counter 1. The ATmega chips don't have this! 45 // Requires a little song and dance to set this up... 46 PLLCSR |= _BV(PLLE); // Enable 64 MHz PLL 47 delayMicroseconds(100); // Allow time to stabilize 48 while(!(PLLCSR & _BV(PLOCK))); // Wait for it...wait for it... 49 PLLCSR |= _BV(PCKE); // Timer1 source = PLL! 50 51 // Enable Timer/Counter 1 PWM, OC1A & !OC1A output pins, 1:1 prescale. 52 GTCCR = TIMSK = 0; // Timer interrupts OFF 53 OCR1C = 255; // 64M/256 = 250 KHz 54 OCR1A = 127; // 50% duty at start = off 55 TCCR1 = _BV(PWM1A) | _BV(COM1A0) | _BV(CS10); 56 57 // Normally the Arduino core library uses Timer/Counter 1 for functions 58 // like delay(), millis(), etc. Having changed the cycle time above, 59 // and turning off the overflow interrupt, these functions won't work 60 // after this. Keeping track of time is our own responsibility now. 61 62 // The Timer/Counter 1 PWM output doesn't time the output square wave; 63 // the frequency (250 KHz) is much too fast for that. Rather, the 64 // speaker itself physically acts as a filter, with the average duty 65 // cycle determining the cone position; center=off, 0,255=extremes. 66 // A separate timer (using the actual sound frequency) then toggles the 67 // duty cycle to adjust amplitude, providing volume control... 68 69 // Configure Timer/Counter 0 for PWM (no interrupt enabled yet). 70 TCCR0A = _BV(WGM01) | _BV(WGM00); // PWM mode 71 72 // An external interrupt (INT0, pin 2 pulled to GND) wakes the chip 73 // from sleep mode to play the sound. 74 MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); // Low level (GND) trigger 75 } 76 77 // ----------------------------------------------------------------------- 78 79 void loop() { 80 81 // To maximize power savings, pins are set to inputs with pull-up 82 // resistors enabled (except for pins 1&4, because LEDs). 83 DDRB = B00000000; PORTB = B00101101; 84 85 // The chip is then put into a very low-power sleep mode... 86 power_all_disable(); // All peripherals off 87 GIMSK |= _BV(INT0); // Enable external interrupt 88 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deepest sleep 89 sleep_mode(); // Stop, will resume here on wake 90 // Code resumes when pin 2 is pulled to GND (e.g. button press). 91 GIMSK &= ~_BV(INT0); // Disable external interrupt 92 93 // Only the two timer/counters are re-enabled on wake. All other 94 // peripheras remain off for power saving. This means no ADC, I2C, etc. 95 power_timer0_enable(); 96 power_timer1_enable(); 97 98 DDRB = B00010011; // Output on pins 0,1 (piezo speaker), 4 (LED) 99 PORTB = B00010000; // LED on 100 101 // Play first note. B5 = 987.77 Hz (round up to 988) 102 pitch(988); // Sets up Timer/Counter 1 for this frequency 103 // The pitch() function configures the timer for 2X the frequency, an 104 // interrupt then alternates between the 'high' and 'low' parts of the 105 // square wave. 988 Hz = 1976 interrupts/sec. 'count' keeps track. 106 // First note is 0.083 sec. 1976 * 0.083 = 164 interrupt counts. 107 // Combined length of notes is 0.92 sec, or 1818 interrupt counts at 108 // this frequency. The amplitude (volume) fades linearly through the 109 // duration of both notes. So this calculates the portion of that drop 110 // through the first note... 111 while(count < 164) quietness = 128L * count / 1818; 112 // This uses fixed-point (integer) math, because floating-point is slow 113 // on the AVR and uses lots of program space. A large integer multiply 114 // (32-bit intermediate result) precedes an integer division, result is 115 // effectively equal to floating-point multiply of 128.0 * 0.0 to 1.0. 116 117 pitch(1319); // Init second note. E6 = 1318.51 Hz, round up to 1319. 118 // 1319 Hz tone = 2638 Hz interrupt. To maintain the duration and make 119 // the volume-scaling math continue from the prior level, counts need to 120 // be adjusted to take this timing change into account. The total 121 // length at this rate would be 2638 * 0.92 = 2427 counts, and first 122 // note duration would have been 2638 * 0.083 = 219 counts... 123 count = 219; 124 // Rather than counting up to the duration, just keep playing until the 125 // effective volume is zero. 126 do { 127 quietness = 128L * count / 2427; 128 } while(quietness < 127); 129 130 // Finished playing both notes. Disable the timer interrupt... 131 TIMSK = 0; 132 133 // Before finishing, the piezo speaker is eased in a controlled manner 134 // from the volume-neutral position (127) to its off position (0) in 135 // order to avoid an audible 'pop' when the code goes to sleep. 136 for(uint8_t i=127; i--; ) { 137 OCR1A = i; // Speaker position 138 for(volatile uint16_t x = F_CPU/32000; --x; ); // Easy, not too fast 139 } 140 } 141 142 // ----------------------------------------------------------------------- 143 144 // These tables list available timer/counter prescaler values and their 145 // configuration bit settings. Normally I'd PROGMEM these, but for these 146 // short tables the code actually compiles a little smaller this way! 147 const uint16_t prescale[] = { 1, 8, 64, 256, 1024 }; 148 const uint8_t tccr[] = { 149 _BV(WGM02) | _BV(CS00), 150 _BV(WGM02) | _BV(CS01), 151 _BV(WGM02) | _BV(CS01) | _BV(CS00), 152 _BV(WGM02) | _BV(CS02), 153 _BV(WGM02) | _BV(CS02) | _BV(CS00), 154 }; 155 156 // Configure Timer/Counter 0 for the requested frequency 157 void pitch(uint16_t freq) { 158 159 uint8_t i; 160 uint16_t f2 = freq << 1; // 2X frequency 161 uint32_t o; 162 163 // Find CPU prescale that can accommodate requested frequency 164 for(i=0; i < (sizeof(prescale) / sizeof(prescale[0])) && 165 (o = (((F_CPU / (uint32_t)prescale[i]) + freq) / 166 (uint32_t)f2) - 1) >= 256L; i++); 167 168 TCCR0B = tccr[i]; // Prescale config bits 169 OCR0A = (uint8_t)o; // PWM interval for 2X freq 170 count = 0; // Reset waveform counter 171 TIMSK = _BV(OCIE0A); // Enable compare match interrupt 172 } 173 174 // TIMER0_OVF vector is already claimed by the Arduino core library, 175 // can't use that. So the compare vector is used instead... 176 ISR(TIMER0_COMPA_vect) { 177 // Bit 0 of count indicates high or low side of square wave. 178 // OCR1A sets average speaker pos, quietness adjusts amplitude. 179 OCR1A = (count++ & 1) ? 255 - quietness : quietness; 180 }