Attiny85 pitch shift with potentiometer

Hey, i've been playing around with a well known pitch shifter using the attiny85 (from here

I've modified a few things so it's a bit whacky but i like it. I'm trying to find a way of replacing the buttons which increment the pitch shift with a potentiometer that sweeps continuously through the range of pitch shift. Is this even possible?

I'm at the limit of my coding abilities/understanding unfortunately. I tried reading a pot and mapping the value and using that value for OCR0A which, from what i can tell is setting the pitch.

I've been reading that apparently i need to do this with an interrupt but I'm a bit stuck as to how to implement that.

Any help would be greatly appreciated.

Thanks



/* Audio Pitch Shifter

   David Johnson-Davies - www.technoblogy.com - 11th February 2017
   ATtiny85 @ 8MHz (internal oscillator; BOD disabled)

   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license:
   http://creativecommons.org/licenses/by/4.0/
*/

volatile uint8_t Buffer[256];             // Circular buffer
volatile uint8_t ReadPtr, WritePtr, LastPtr, New;

// Everything done by interrupts **********************************

// ADC conversion complete - save sample in buffer
ISR (ADC_vect) {
  Buffer[LastPtr] = New;
  New = ADCH + 128;
  Buffer[WritePtr] = (Buffer[WritePtr] + New) >> 1;
  LastPtr = WritePtr;
  WritePtr = (WritePtr - 5) & 0xFF;
}

// Timer interrupt - read from buffer and output to DAC
ISR (TIMER0_COMPA_vect) {
  OCR1A = Buffer[ReadPtr];
  ReadPtr = (ReadPtr + 5) & 0xFF;
}




// Setup **********************************************

void setup () {
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1 << PCKE | 1 << PLLE;

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                              // Timer interrupts OFF
  TCCR1 = 1 << PWM1A | 2 << COM1A0 | 1 << CS10; // PWM OCR1A, clear on match, 1:1 prescale
  OCR1A = 67;
  pinMode(1, OUTPUT);                     // Enable OC1A PWM output pin

  // Set up Timer/Counter0 to generate 20kHz interrupt
  TCCR0A = 2 << WGM00;                    // CTC mode
  TCCR0B = 2 << CS00;                     // /8 prescaler
  OCR0A = 18;                             // 17.9kHz interrupt
  TIMSK = TIMSK | 1 << OCIE0A;            // Enable interrupt

  // Set up ADC
  ADMUX = 2 << REFS0 | 1 << ADLAR | 7 << MUX0; // Internal 1.1V ref, ADC2 vs ADC3, x20
  // Enable, auto trigger, interrupt, 250kHz ADC clock:
  ADCSRA = 1 << ADEN | 1 << ADSC | 1 << ADATE | 1 << ADIE | 4 << ADPS0;
  ADCSRB = 1 << 7 | 0 << ADTS0;           // Bipolar, free-running


}



// Enable pin change interrupt


void loop () {
}

If you use a potentiometer, you will interfere with the main operation of the ADC and, accordingly, the main program. It might be more painless to use a rotary encoder.

Interesting, i'm definitely not against that idea. Do you mind explaining how that simplifies things and how i might approach using a rotary encoder?

Thanks a lot

Made some progress and got it kind of working with the rotary encoder. It shifts the pitch down in one direction but i can't seem to make it shift the pitch up when the encoder is moves in the other direction. What am i missing here?

//Good settings - OCR0A = 18 + 3<<ADPS0;
//              - OCR0A = 67 + 4<<ADPS0
//              - OCR0A = 27 + 5<<ADPS0


/* Audio Pitch Shifter

   David Johnson-Davies - www.technoblogy.com - 11th February 2017
   ATtiny85 @ 8MHz (internal oscillator; BOD disabled)

   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license:
   http://creativecommons.org/licenses/by/4.0/
*/

volatile uint8_t Buffer[256];             // Circular buffer
volatile uint8_t ReadPtr, WritePtr, LastPtr, New;
const int EncoderA = 0;
const int EncoderB = 2;
volatile int a0;
volatile int c0;
volatile int Count = 0;

// Everything done by interrupts **********************************

// ADC conversion complete - save sample in buffer
ISR (ADC_vect) {
  Buffer[LastPtr] = New;
  New = ADCH + 128;
  Buffer[WritePtr] = (Buffer[WritePtr] + New) >> 1;
  LastPtr = WritePtr;
  WritePtr = (WritePtr - 5) & 0xFF;
}

// Timer interrupt - read from buffer and output to DAC
ISR (TIMER0_COMPA_vect) {
  OCR1A = Buffer[ReadPtr];
  ReadPtr = (ReadPtr + 5) & 0xFF;
}

// Pin change interrupt adjusts shift
ISR (PCINT0_vect) {
  int a = PINB>>EncoderA & 20;
  int b = PINB>>EncoderB & 20;
  if (a != a0) {
   OCR0A++;
    if (b != c0) {
      OCR0A--;
    }
  } 
}



// Setup **********************************************

void setup () {
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1 << PCKE | 1 << PLLE;

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                              // Timer interrupts OFF
  TCCR1 = 1 << PWM1A | 2 << COM1A0 | 1 << CS10; // PWM OCR1A, clear on match, 1:1 prescale
  OCR1A = 67;
  pinMode(1, OUTPUT);                     // Enable OC1A PWM output pin

  // Set up Timer/Counter0 to generate 20kHz interrupt
  TCCR0A = 2 << WGM00;                    // CTC mode
  TCCR0B = 2 << CS00;                     // /8 prescaler
  OCR0A = 57;                             // 17.9kHz interrupt
  TIMSK = TIMSK | 1 << OCIE0A;            // Enable interrupt

  // Set up ADC
  ADMUX = 2 << REFS0 | 1 << ADLAR | 7 << MUX0; // Internal 1.1V ref, ADC2 vs ADC3, x20
  // Enable, auto trigger, interrupt, 250kHz ADC clock:
  ADCSRA = 1 << ADEN | 1 << ADSC | 1 << ADATE | 1 << ADIE | 5 << ADPS0;
  ADCSRB = 1 << 7 | 0 << ADTS0;           // Bipolar, free-running

// Set up buttons on PB0 and PB2
  pinMode(EncoderA, INPUT_PULLUP);
  pinMode(EncoderB, INPUT_PULLUP);
  PCMSK = 1<<PINB0 | 1<<PINB2;            // Pin change interrupts on PB0 and PB2
  GIMSK = GIMSK | 1<<PCIE;                // Enable pin change interrupt

}

void loop () {
}

I don't understand about interrupts but here you are using variables for comparison that you only defined at the beginning but you didn't even set a value to them (a0 and c0). Also, it's good to have a check of the OCR0A value as a lower and upper limit to avoid other problems.

Thanks for the reply. For the rotary encoder interrupt i used this example:
(There's a fair amount of irrelevant stuff for a display which i'm not using)

/* Bounce-Free Rotary Encoder

   David Johnson-Davies - www.technoblogy.com - 28th October 2017
   ATtiny85 @ 1 MHz (internal oscillator; BOD disabled)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license: 
   http://creativecommons.org/licenses/by/4.0/
*/

const int DataOut = 0;
const int ClockPin = 1;
const int LED = 2;
const int EncoderA = 3;
const int EncoderB = 4;

// Serial seven segment display **********************************************

const int Clear_Display = 0x76;
const int Cursor_Control = 0x79;
const int Brightness_Control = 0x7A;

void SendByte(char data) {
  shiftOut(DataOut, ClockPin, MSBFIRST, data);
}

// Clear display
void ClearDisplay () {
  SendByte(Clear_Display);
  SendByte(Brightness_Control);
  SendByte(255);
}

// Display byte
void Display (int number) {
  boolean dig = false;
  int j;
  SendByte(Cursor_Control); SendByte(0);
  j = 1000;
  for (int d=0; d<4 ; d++) {
    int i = (number/j) % 10;
    if (!i && !dig && j>1) SendByte(' ');
    else { SendByte(i); dig = true; }
    j=j/10;
  }
}

// Rotary encoder **********************************************

volatile int a0;
volatile int c0;
volatile int Count = 0;

// Called when encoder value changes
void ChangeValue (bool Up) {
  digitalWrite(LED, Up);
  Count = max(min((Count + (Up ? 1 : -1)), 1000), 0);
}

// Pin change interrupt service routine
ISR (PCINT0_vect) {
  int a = PINB>>EncoderA & 1;
  int b = PINB>>EncoderB & 1;
  if (a != a0) {              // A changed
    a0 = a;
    if (b != c0) {
      c0 = b;
      ChangeValue(a == b);
    }
  }
}

// Setup demo **********************************************

void setup() {
  pinMode(ClockPin, OUTPUT);
  pinMode(DataOut, OUTPUT);
  digitalWrite(ClockPin, LOW);
  digitalWrite(DataOut, LOW);
  pinMode(LED, OUTPUT);
  ClearDisplay();
  pinMode(EncoderA, INPUT_PULLUP);
  pinMode(EncoderB, INPUT_PULLUP);
  PCMSK = 1<<EncoderA;        // Configure pin change interrupt on A
  GIMSK = 1<<PCIE;            // Enable interrupt
  GIFR = 1<<PCIF;             // Clear interrupt flag
}

void loop() {
  Display(Count);
  delay(100);
}

Perhaps i have copied this incorrectly, as in this original it states:

if (a != a0) {              // A changed
    a0 = a;

I just tried adding that into the code, as well as the c0 = b; part but didn't seem to make any difference

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.