ATtiny85 - 'pausing' a voltage controlled oscillator

Hi there,

I've been chipping away at this on and off for months, I'm hoping someone can suggest a better solution than I've managed.

I'm using an ATiny85 as a pulse wave oscillator, running at audio frequencies with the frequency set by a voltage on pin 7. I need the output to go (and stay) low when the voltage on pin 7 exceeds a certain threshold (say, 4v although it's not hugely critical) and when the voltage drops below that threshold again, the oscillator needs to pick back up again.

I'm struggling mainly with the interaction (probably the wrong word?) between the code required to set up and run the oscillator and then using 'normal' Arduino code afterwards to set the oscillator frequency etc. Any assistance would be amazing.

This seems to work but not consistently:

volatile unsigned int Acc;  // You need this for waveform creation
volatile unsigned int Freq; // the multiplier (read from pot) that sets the signal frequency
volatile unsigned int Jump; // multiple of Freq sets the signal frequency
void setup() {
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1 << PCKE | 1 << PLLE;

  CLKPR = (1 << CLKPCE);
  CLKPR = 0; // Don't divide. Gives us the top speed of 31Khz

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                     // Timer interrupts OFF
  TCCR1 = 1 << PWM1A  | 2 << COM1A0 | 1 << CS10;         // 1:1 prescale
  GTCCR = 1 << PWM1B | 2 << COM1B0; // PWM B, clear on match
  pinMode(1, OUTPUT);            // Enable PWM output pin (physical pin 6)

  // Set up Timer/Counter0 for 20kHz interrupt to output samples.
  TCCR0A = 7 << WGM00;           // Fast PWM
  TCCR0B = 1 << WGM02 | 1 << CS00; // no prescale
  TIMSK = 1 << OCIE0A;           // Enable compare match, disable overflow
  OCR0A = 13;                    // Main multiplier for osc freq
  pinMode (A1, INPUT);           // Enable analogue input for pot  (physical pin 7)

}

void loop() {
  {
    Freq = analogRead(A1);
    Freq = map(Freq, 0, 1023, 200, 1000); //map pot to frequency
    Jump = Freq * 6;

  }
}

ISR(TIMER0_COMPA_vect) {

  if (Freq <= 899) {
    Acc = Acc + Jump;
    int8_t temp = Acc >> 8;
    temp = temp & temp << 1;
    OCR1A = temp >> 7;
  }
  else if (Freq >= 900) {
    digitalWrite (1, LOW);
  }



}

Unless you stop the timer by setting the clock select to 000 or turn off the PWM output by setting the Compare Output Mode to 00, the clock will keep toggling pins OC1A and OC1B.

That's really useful, thanks very much.

So can I substitute the "digitalWrite (1, LOW)" idea with either of your two suggestions? Setting the clock select to 000 sounds less drastic but is there any practical difference?

I've found a fair amount of information about setting the oscillation going with these things and nothing about stopping it!

The timer runs when you set the CS bits to 001 (no prescale). Setting them back to 000 is probably the easiest way to stop the timer. I think it will leave the output pin in whatever state it was in. You probably have to set the COM bits to 00 and use digitalWrite() if you want the pin LOW.

1 Like

IIRC digitalWrite also disable PWM. Or it works only for PWM from analogWrite?

1 Like

OK, I've spent the best part of a week now trying to work out how to implement your suggestion!

I'm pretty much there I think except...once the output is set low, the timer won't restart when the voltage on pin 7 drops under the threshold again. Is that likely to just be down to the order in which I've set things to run? I.e the timer stuff happening beyond the loop?

volatile unsigned int Acc;  // You need this for waveform creation, it's an abbreviation of 'accumulator'
volatile unsigned int Freq; // the multiplier (read from pot) that sets the signal frequency
volatile unsigned int Jump; // multiple of Freq sets the signal frequency
void setup() {
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1 << PCKE | 1 << PLLE;

  CLKPR = (1 << CLKPCE);
  CLKPR = 0; // Don't divide. Gives us the top speed of 31Khz

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                     // Timer interrupts OFF
  TCCR1 = 1 << PWM1A  | 2 << COM1A0 | 1 << CS10;         // 1:1 prescale
  GTCCR = 1 << PWM1B | 2 << COM1B0; // PWM B, clear on match
  pinMode(1, OUTPUT);            // Enable PWM output pin (physical pin 6)
  
  // Set up Timer/Counter0 for 20kHz interrupt to output samples.
  TCCR0A = 7 << WGM00;           // Fast PWM
  TCCR0B = 1 << WGM02 | 1 << CS00; // no prescale
  TIMSK = 1 << OCIE0A;           // Enable compare match, disable overflow
  OCR0A = 13;                    // Main multiplier for osc freq
  pinMode (A1, INPUT);           // Enable analogue input for pot  (physical pin 7)

}

void loop() {
  {
    Freq = analogRead(A1);
    Freq = map(Freq, 0, 1023, 200, 1000); //map pot to frequency
    Jump = Freq * 6;

  }
}

ISR(TIMER0_COMPA_vect) {

  if (Freq <= 899) {
    Acc = Acc + Jump;
    int8_t temp = Acc >> 8;
    temp = temp & temp << 1;
    OCR1A = temp >> 7;
  }
  else if (Freq >=900) {
    
  TCCR1 &= ~(1<< CS12);
  TCCR1 &= ~(1<< CS11);
  TCCR1 &= ~(1<< CS10);
  TCCR1|=(0<<COM1B0)|(0<<COM1A0);
  digitalWrite (1, LOW);
  }



}

This line does nothing. You can't set a bit on or off by OR'ing with a zero.

These lines stop the timer by setting the Clock Select bits to zero. That stops the interrupts. Most of your code is in the interrupt handler. Move the code to turn the clock on and off to the loop() function.

1 Like