reading pots to adjust delay or delayext

Status
Not open for further replies.

vangalvin

Member
I have been working on a project for my son who has just moved from an acoustic guitar to electric guitar. The teensy 3.2 seems to be a great platform to build an effects pedal that has all sorts of features. One of the issues I have had is when using ADC1 for the pots, I seem to be getting a little bit of jitter that causes the delay to click. So i implemented some smoothing. I did try and use a resistor and cap to smooth the signal but still noticed the occasional click on occasion.

Here is a copy of the code that I am using but was wondering if I could move the smoothing routine in to a class so I do not need to have the code repeated for each pot. My programming skills are not overly advanced and although I have written a few of my own functions and some really basic classes I was unsure if the variables set for the class stay with the object created.

Here is the code that seems to work prety well for me.
Code:
#include <ADC.h>        //ADCZ to read the POT values
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>     //Button Library


ADC *adc = new ADC();; // adc object
Bounce button0 = Bounce(2, 15);


// GUItool: begin automatically generated code
AudioInputUSB            usb1;           //xy=68,39
AudioMixer4              mixer1;         //xy=209,51
AudioEffectDelayExternal dly(AUDIO_MEMORY_23LC1024); //xy=382,215
AudioMixer4              mixer2;         //xy=682,49
AudioOutputUSB           usb2;           //xy=1027,37
AudioOutputAnalog        dac1;           //xy=1027,85
AudioConnection          patchCord1(usb1, 0, mixer1, 0);
AudioConnection          patchCord2(usb1, 1, mixer1, 1);
AudioConnection          patchCord3(mixer1, dly);
AudioConnection          patchCord4(mixer1, 0, mixer2, 0);
AudioConnection          patchCord5(dly, 0, mixer2, 1);
AudioConnection          patchCord6(dly, 7, mixer1, 3);
AudioConnection          patchCord7(dly, 7, mixer2, 2);
AudioConnection          patchCord8(mixer2, 0, usb2, 0);
AudioConnection          patchCord9(mixer2, 0, usb2, 1);
AudioControlSGTL5000     audioShield;    //xy=81,135
// GUItool: end automatically generated code


const int ledPin = 13;
int knob1 = A16;          // Effects Knob
int knob2 = A17;          // Effects Knob
int knob3 = A18;          // Effects Knob




#define SDCARD_CS_PIN    10   // CS for Caltex Card
#define SPIRAM_CS_PIN    6    //CS for SRAM
//#define SDCARD_MOSI_PIN  7   //MOSI on Caltex Card
//#define SDCARD_MISO_PIN  12   //MISO on Caltex Card
//#define SDCARD_SCK_PIN   14   //SCK on caltex card


void set_adc1_to_3v3(){
  ADC1_SC3 = 0; // cancel calibration
  ADC1_SC2 = ADC_SC2_REFSEL(0); // vcc/ext ref 3.3v


  ADC1_SC3 = ADC_SC3_CAL;  // begin calibration


  uint16_t sum;


  //serial_print("wait_for_cal\n");


  while( (ADC1_SC3 & ADC_SC3_CAL))
  {
    // wait
  }


  __disable_irq();


    sum = ADC1_CLPS + ADC1_CLP4 + ADC1_CLP3 + ADC1_CLP2 + ADC1_CLP1 + ADC1_CLP0;
    sum = (sum / 2) | 0x8000;
    ADC1_PG = sum;
    sum = ADC1_CLMS + ADC1_CLM4 + ADC1_CLM3 + ADC1_CLM2 + ADC1_CLM1 + ADC1_CLM0;
    sum = (sum / 2) | 0x8000;
    ADC1_MG = sum;


  __enable_irq(); 
}


void setup() {
    pinMode(SDCARD_CS_PIN, OUTPUT);   //Enable SD CS pin for output
    pinMode(SPIRAM_CS_PIN, OUTPUT);   //Enable SRAM CS pin for output
    digitalWrite(SDCARD_CS_PIN, HIGH); //Make sure the pin is HIGG on start up
    digitalWrite(SPIRAM_CS_PIN, HIGH); //Make sure the pin is HIGG on start up
    
        AudioMemory(50);
//Setup For Delay 
//mixer1.gain(0, 1);
//mixer1.gain(1, 1);
//mixer2.gain(0, 0.7);
//mixer2.gain(1, 0.7);


//Setup for Loop
mixer1.gain(0, 0.5);
mixer1.gain(1, 0.5);
mixer1.gain(2, 0);
mixer1.gain(3, 0.7);


mixer2.gain(0, 1);
mixer2.gain(1, 0);
mixer2.gain(2, 1);
mixer2.gain(3, 0);


// All this just to check one POT
adc->setAveraging(16, ADC_1); // set number of averages
adc->setResolution(8, ADC_1); // set bits of resolution
adc->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_LOW_SPEED, ADC_1);
adc->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED, ADC_1);                                                                                                                                                                                                                                                                                                                                                         adc->setReference( ADC_REFERENCE::REF_3V3, ADC_1 );
set_adc1_to_3v3();


delay(200);


        dly.delay(0, 0);


}


float mapfloat(float x, float in_min, float in_max, float out_min, float out_max){
 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


float NEW_WEIGHT = 0.2;
float SPD_WEIGHT = 0.4;
int k1val = 0;
int k2val = 0;
int k3val = 0;
int k1val_old = 0;
int k2val_old = 0;
int k3val_old = 0;
int k1diff = 0;
int k2diff = 0;
int k3diff = 0;
int k1diff_speed = 2;
int k2diff_speed = 2;
int k3diff_speed = 2;
int k1val_last = 0;
int k2val_last = 0;
int k3val_last = 0;
void loop() {


k1val = adc->analogRead(knob1, ADC_1);
k2val = adc->analogRead(knob2, ADC_1);
k3val = adc->analogRead(knob3, ADC_1);


k1diff = NEW_WEIGHT * k1val + (1-NEW_WEIGHT) * (k1diff + k1diff_speed);   //updates diff_value, our smoothed pot outputs. Note that diff_speed is used too!
k1diff_speed = SPD_WEIGHT * (k1diff - k1val_old) + (1-SPD_WEIGHT) * k1diff_speed;  //updates diff_speed, or the rate of change of the smoothed output
k1val_old = k1diff;  //diff_old_value is to let us calculate the speed


k2diff = NEW_WEIGHT * k2val + (1-NEW_WEIGHT) * (k2diff + k2diff_speed);   //updates diff_value, our smoothed pot outputs. Note that diff_speed is used too!
k2diff_speed = SPD_WEIGHT * (k2diff - k2val_old) + (1-SPD_WEIGHT) * k2diff_speed;  //updates diff_speed, or the rate of change of the smoothed output
k2val_old = k2diff;  //diff_old_value is to let us calculate the speed


k3diff = NEW_WEIGHT * k3val + (1-NEW_WEIGHT) * (k3diff + k3diff_speed);   //updates diff_value, our smoothed pot outputs. Note that diff_speed is used too!
k3diff_speed = SPD_WEIGHT * (k3diff - k3val_old) + (1-SPD_WEIGHT) * k3diff_speed;  //updates diff_speed, or the rate of change of the smoothed output
k3val_old = k3diff;  //diff_old_value is to let us calculate the speed


if((k1diff >= k1val_last + 4) || (k1diff <= k1val_last - 4)){
  k1val_last = k1diff;
  Serial.print("Change Delay : ");
  Serial.println(k1diff);
  dly.delay(7, map(k1val, 0, 255, 0, 1400));
}


if((k2diff >= k2val_last + 4) || (k2diff <= k2val_last - 4)){
  k2val_last = k2diff;
  Serial.print("Change Mixer Port 0 : ");
  Serial.println(k2diff);
//  mixer2.gain(0, mapfloat(k2diff, 0, 255, 0.001, 1.001));
  mixer1.gain(0, mapfloat(k2diff, 0, 255, 0.001, 1.001));
  mixer1.gain(1, mapfloat(k2diff, 0, 255, 0.001, 1.001));


mixer1.gain(3, 0.7);
}


if((k3diff >= k3val_last + 4) || (k3diff <= k3val_last - 4)){
  k3val_last = k3diff;
  Serial.print("Change Mixer Port 1 : ");
  Serial.println(k3diff);
//  mixer2.gain(1, mapfloat(k3diff, 0, 255, 0.001, 1.001));
  mixer1.gain(3, mapfloat(k3diff, 0, 255, 0.001, 1.001));
}


  delay(100);
}//end loop

And this was the class I was thinking about setting up to handle the smoothing.
do the variables in the class become unique to the object or are they shared between the objects. For instance if an object is created such as
ctrlKnob kv1(A16, 0);

then it was updated using something like..
update(adc->analogRead(knob1, ADC_1));

would the variable lastknobval be set inside the kv1 object until the update is called again?
is the variable unique to kv1 or if another object was created would setting the variable in the kv1 object also change the variable for other objects created?

Code:
class ctrlKnob {
  int knob;           // the pin of the knob (A16, A17, A18)
  int knobval = 0;        // the value of the knob
  int lastknobval = 0;    // previous knob value
  float NEW_WEIGHT = 0.6;
  float SPD_WEIGHT = 0.7;
  int diff_speed = 1;
  
  public:
  ctrlKnob(int knob, int knobval){
     lastknobval = knobval;
     Serial.println(knobval);
  }
  
 int Update(int value){


//diff_value is the smoothed output from the pots
//value is the new reading 
//diff_speed is the rate of change of the pot value


Serial.print(" lastknobval: ");
Serial.print(lastknobval);
Serial.print(" ");
//NEW_WEIGHT and SPD_WEIGHT are constants (range: 0 to 1) used to adjust how much the filter filters out: lower is more filtering
  knobval = NEW_WEIGHT * value + (1-NEW_WEIGHT) * (knobval + diff_speed);   //updates diff_value, our smoothed pot outputs. Note that diff_speed is used too!
  knobval = SPD_WEIGHT * (knobval - lastknobval) + (1-SPD_WEIGHT) * diff_speed;  //updates diff_speed, or the rate of change of the smoothed output
  lastknobval = knobval;  //diff_old_value is to let us calculate the speed
  //Serial.println("knobval");
 
  return(knobval);
  }
};
 
Why don't you use ALPS rotary encoders which are more precise and long-term stable ?

I can see many bonuses in doing this on the next project, however for this one I was trying to use what I had in my parts bin. I have not yet played around with encoders but after seeing the i2c alps encoder that is looking like a great idea.

Thank you for the suggestion.
 
1... I think you should read the pots at startup and set gains on their current value or you'll click on the first move (that or make it wait until the default is "picked up" by the knob


2... consider ResponsiveAnalogRead() to smooth the gain readings
 
1... I think you should read the pots at startup and set gains on their current value or you'll click on the first move (that or make it wait until the default is "picked up" by the knob

The initial click when moving the pot is not too much of an issue as once you have the level set it should be nice and stable at that level. setting the pot to get to the desired delay etc will only be done during the setup phase and one it is set and the reading is stable it should not need to keep updating the delay function.


2... consider ResponsiveAnalogRead() to smooth the gain readings
I will give that a go, looks like it will be a lot smoother than what I was doing :)
Thanks for that.

That should allow for a better "only update if pot value has changed" routine.
 
Status
Not open for further replies.
Back
Top