I knew the ADC has a clock, so I decided to see if I could put those together and make some noise while avoiding use of the 8-bit timers nor the watchdog timer.
So only setup and nothing in loop.
That turned out to become a timerless beep tone.
Then I added a few lines in the loop, to demonstrate two tones and turn the beep on and off.
Otherwise it would have been a complete useless beep.
Here is the sketch:
/*************************************************************************
* Attiny85 two tone beeper on SCL pin PB2 (mis)using only ADC and USI
* Uses none of the 8-bit timers or watchdog timer)
* Connect a passive beeper to SCL pin PB2
* Works on 16MHz or 8Mhz
*************************************************************************/
void setup() {
DDRB |= _BV(PB2);//set SCL as output
USICR |= _BV(USIOIE);// USI 4 bit Counter Overflow Interrupt Enable
// ADC Internal 1.1V reference and measure the internal temp sensor
ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
// Enable the ADC in free running mode and set interrupt on. Prescaler factor 16
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2);
}
void loop() {
ADCSRA |= _BV(ADPS0); // Increase ADC presacaler factor from 16 to 32 for Low tone
delay(400);
ADCSRA &= ~_BV(ADPS0); // Decrease prescaler factor back to 16 for High tone
delay(400);
ADCSRA &= ~_BV(ADIE); // turn off the sound by disabling the ADC interrupt.
delay (1000);
ADCSRA |= _BV(ADIE); // and turn it on again by enabling the interrupt
}
ISR(USI_OVF_vect) { //interrupt service routine when USI timer overflows
USICR |= _BV(USITC);// toggle the SCL port by writing one to this bit
USISR |= _BV(USIOIF);// clear the USI Counter Overflow Interrupt Flag
}
ISR(ADC_vect) { //interrupt service routine when ADC completes conversion
USICR |= _BV(USICLK);// strobe the USI clock
}
I don't think the USI can be used while doing this, but the ADC could, as long as you keep it in free running mode.
For some odd reason I could only get it to work on 16 and 8 MHz. When trying 4 MHz I expected the tone frequency of the beep go in half again, but it dropped dramatically. No clue what's going on there. [edit: looks like a bug in V2.0.0. of Attinycore)
I used a passive piezo buzzer and the output is fixed at the USI CLK pin (PB2).
The ADC is measuring the internal temp sensor now, so I could have made a beeping temperature alarm for if the tiny gets too hot.
I see little practical value, but it was fun to make.
From your post, I have not clearly understood the objectives of your project. I hope that getting answers to the following queries will help me for better understanding to practice your project.
1. If you are generating two tones at PB2-pin of the ATtiny85, then -- (1) What is the frequency and duration of the 1st tone? (2) What is the frequency and duration of the 2nd tone? (3) How much time gap between the two tones?
2. About operating frequency of the ATtiny85: (1) Are you using external 16 MHz crystal and CKDIV fuse bit unprogrammed? (2) Are you using external 8 MHz crystal and CKDIV fuse bit unprogrammed? (3) Are you using internal 8 MHz oscillator will PLL multiplier and CKDIV fuse bit unprogrammed?
3.
What is this 4 MHz frequency -- is it an external crytal you are connecting with ATtiny85.
4. Which core you are using and what are the values of the fuse bits descrbed in that core? Where can I find the listing of the core?
The objective was to pass some time playing with an ATtiny85 until I am up and running again. It's better than solving Sudoku's
Clocked at 16MHz PLL (L:0xF1, H:0xD7), the frequency of the high tone is 3480Hz and the low tone is 1416Hz
Clocked at 8MHz OSC (L:0xE2, H:0xD7) the frequency of the high tone is 1422Hz and the low tone is 657Hz
I was using V2.2.0. (Github) version of Attinycore. This is the development version, which has not yet been released in the board manager.
And that was the problem why I could not get it to work on 4MHz clock. When I use Attinycore V1.5.2 that is published via the boards manager it works as expected on 4MHz OSC (L:0x62, H:0xD7) and I get 657Hz high tone and 316Hz low tone.
The 4MHz delays are also wrong on my Attinycore V2.0.0, so I will test that a bit more and log a bug in Github.
On all clock speeds there is no gap between switching from high tone to low tone. that is instant. The one second delay after turning off the ADC interrupt creates a one second silence ofcourse, but when I delete these lines
ADCSRA &= ~_BV(ADIE); // turn off the sound by disabling the ADC interrupt.
delay (1000);
ADCSRA |= _BV(ADIE); // and turn it on again by enabling the interrupt
I get a two-tone beeper that keeps going. Decrease the delays from 400 to 20 and you have a ringtone.
And you can still use the ADC to do some measurements. So lets misuse the 3rd peripheral the RESET pin PB5.
Keep the passive beeper connected to SCL pin PB2
Connect a (e.g. 10K) potmeter between GND <> RESET <> VCC, with the center pin of the potmeter to RESET. No need to disable the reset pin's reset function. One half turn of the potmeter will obviously reset the ATTiny, but the other half can be measured by the ADC, which will demonstrate itself with "no tone <> low tone <> high tone" And when you turn the potmeter too far you will hear that. It will stop the Tiny high tone by keeping it in reset.
/*************************************************************************
Attiny85 two tone beeper on SCL pin PB2 (mis)using only ADC and USI
Uses none of the 8-bit timers or watchdog timer)
Connect a passive beeper to SCL pin PB2
Connect a (10K) potmeter between GND<>RESET<>VCC
*************************************************************************/
void setup() {
DDRB |= _BV(PB2);//set SCL as output
USICR |= _BV(USIOIE);// USI 4 bit Counter Overflow Interrupt Enable
ADMUX = _BV(ADLAR); // left adjust ADC result for only using ADCH connect to RESET
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADPS2); // Enable ADC in free running mode.
}
void loop() {
if (ADCH > 250) ADCSRA &= ~_BV(ADIE); // turn off sound by disabling the ADC interrupt.
if (ADCH < 240 && ADCH > 200) ADCSRA |= _BV(ADIE) | _BV(ADPS0); // turn on Low tone
if (ADCH < 190) ADCSRA &= ~_BV(ADPS0); // change to High tone
}
ISR(USI_OVF_vect) { //interrupt service routine when USI timer overflows
USICR |= _BV(USITC);// toggle the SCL port by writing one to this bit
USISR |= _BV(USIOIF);// clear the USI Counter Overflow Interrupt Flag
}
ISR(ADC_vect) { //interrupt service routine when ADC completes conversion
USICR |= _BV(USICLK);// strobe the USI clock
}
1. When LF (Low Fuse bots) = 0x62 (0110 0010), CKDIV is programmed and 8 MHz Internal clock frequency. As a resuly, the operating frequency is 8 MHz/8 = 1 MHz. Why are you saying about 4 MHz?
2. What are the durations of high and low tones?
3. How have derived the 657 Hz frequencey for high tone and 316 Hz for low tone?
4. Which Programmer are you using to store the codes insdie the Flash of ATiny85 -- AVR programmer, USBasp or UNO as ISP?
At MCU startup the CKDIV being programmed causes the clock prescaler initial value to be set at a division factor 8. Nothing prevents your code from clearing CLKPS1 after startup to change that into a division factor 2 to get 4MHz (or change it into whatever prescaler value with subsequent internal system clock frequency you want).
In my second sketch the duration is defined by how long you keep the potmeter at a certain position. Or I do not understand your question, you may mean something else with the word "durations" (pulsewidth, period or frequency perhaps?)
I hooked up my pulsewidth/frequency analyzer
USBasp
The role of the ADC is twofold. The ADC clock is used for generating a clock source for the USI timer and the ADC is used at the same time to measure the voltage of the reset pin.
I made an example that demonstrates that by changing the cpu prescaling on the fly, you will see the blink frequency (led to PB2) changing.
#include <avr/delay.h>
int main(void) {
DDRB |= _BV(PB2);// set output pin
while (1) {
for (int a = 5; a >= 0; a--) {
CLKPR = _BV(CLKPCE); // Enable clock prescaler change
CLKPR = a; // change clock prescaler
for (int b = 0; b < (12 - 2 * a); b++) { // blink variable times
PORTB ^= _BV(PB2); // toggle pin
_delay_ms(50);
}
}
}
}
The second for loop makes it blink a few times more on low prescaling factors, or it would be difficult to see. I skipped the 128 and 256 prescaling factors or it would be too boring to look at.