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.
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