how to convert from pot to encoder

Hi, I made some changes to help me keep track of things and think it is going in the right direction but cannot test it until tomorrow. I changed the code to make barWidth = pulseWidth * widthValue; so that barWidth will = the time in uS. Seem right to you?

// update 7/14/2017 added correct math and setting for position of pickup
// revision 10 10/28/2016 adds charging pulse with Timer 2
// lowest RPM = 615 with charge pulse to keep OCR2A <= 255
// CDI Tester Pulse Generator Serial Output Arduino Code
// 8/17 added code for pulse out width

int pot1 = A3; // select the input pin for the pot for rpm
int pot2 = A4; // select the input pin for the pot for pickup location in degrees
int pot3 = A5; // select the input pin for the pot for width of bar on flywheel in degrees
int rpmValue = 0; // variable to store the value coming from the sensor //was potValue
int positionValue = 0; // position of pickup in degrees // was pickupValue
int widthValue = 0; // variable to set width of bar on flywheel //was barValue

int timerTopValue = 12500; // changed from timerTopValue = 0
int outputPin = 4; // select the pin for the output for trigger pulse, changed from 8
int chargePin = 7; // select the pin for the output for charge pulses

volatile boolean trigger = false;
volatile unsigned long delayPeriod;
unsigned long copy_delayPeriod;
volatile unsigned long delayPeriodStart;
float delayDegrees; // changed from int to float for decimal place display
int RPM;
int pickupPos; // pickup position in degrees
int barWidth; // bar width in degrees
int pulseWidth; // pulse width in degrees                                            
volatile boolean interruptFlag;
unsigned long analogReadInterval = 1000; //read pots and map
unsigned long lastAnalogRead;

const byte setFallingSwitch = 5;
char risefall = 'R'; //default rising mode

const byte setChargePulseSwitch = 6;
boolean chargePulse = false ;  //default dc powered mode

volatile byte timeSliceCount = 0; //TDC 0 degrees

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// set the LCD address to 0x3f for a 20 chars 4 line display
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

void setup() {
  //Serial.begin(115200);
  //Serial.println("starting...");
  lcd.begin(20, 4); // initialize the lcd for 20 chars 4 lines, turn on backlight
  lcd.setCursor(1, 0); // lcd display setup of unchanging headings
  lcd.print("RPM:"); // print fixed characters
  lcd.setCursor(12, 0);
  lcd.print("Mode:"); //to display code choice R or F
  lcd.setCursor(1, 1);
  lcd.print("Pos:");
  lcd.setCursor(9, 1);
  lcd.print("Width:");
  lcd.setCursor(1, 2);
  lcd.print("Us Delay:");
  lcd.setCursor(1, 3);
  lcd.print("Deg Advance:");

  pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
  pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT
  pinMode (setFallingSwitch, INPUT_PULLUP);//check for a LOW input to indicate switch is closed to ground
  pinMode(setChargePulseSwitch, INPUT_PULLUP);

  if (digitalRead(setChargePulseSwitch) == LOW)
    chargePulse = true; //AC CDI

  //Timer1 default set up .5ms trigger pulse every 50 ms
  TCCR1A = 0;
  TCCR1B = (1 << WGM12); //CTC mode to OCR1A
  OCR1A = timerTopValue;
  TIMSK1 |= (1 << OCIE1A); //interrupt enable compareA
  TIMSK1 |= (1 << OCIE1B); //interrupt enable compareB
  OCR1B = 125; //sets trigger pulse width, 500 = 2Ms, 250 = 1Ms, 125 = .5Ms, set to 1250 for 5013
  TCCR1B |= (1 << CS11) | (1 << CS10); //prescaler 64 4us/tick

  //Timer2 default setup charge pulse 12 periods 30 degrees each; only 5 get turned on
  //charge pulse timing interval = Timer1 trigger period/12 = timerTopValue/96
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A = 1 << WGM20; //lsb of mode 7 pwm to OCR2A
  TCCR2B = 1 << WGM22; //msb of mode 7 pwm to OCR2A;
  OCR2A = timerTopValue / 96; //96 = 12*4*2 (#periods, prescaler difference, up/down timer mode )
  TIMSK2 = 0;
  TIMSK2 = 1 << TOIE2; //enable overflow interrupt
  // actually start timer in ISR(TIMER1_COMPA_vect
  // to prevent out of sync charge pulse, no prescaler set here

  attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, FALLING);

}

void loop()
{
  if (millis() - lastAnalogRead >= analogReadInterval)
  {
    lastAnalogRead += analogReadInterval;
    rpmValue = analogRead(pot1); // rpm
    positionValue = analogRead(pot2); // pickup position
    widthValue = analogRead(pot3); // bar width  
  
    //Timer2 OCR2A requires max value 255 => 615 lowest RPM
    if (chargePulse)
    {
      RPM = map(rpmValue, 0, 1023, 615, 3800); // rpm range
      pickup = map(positionValue, 0, 1023, 0, 65); // to set position of pickup in degrees
      pulseWidth = (60000000/RPM)/360; // time for 1° in uS
      barWidth = pulseWidth * widthValue; //result is in uS based on width of bar in degrees
      //RPM = 615; //for my serial test purposes 615-3800
    }
    else
    {
      RPM = map(potValue, 0, 1023, 500, 3800);
      pickup = map(positionValue, 0, 1023, 0, 65); // to set position of pickup in degrees
      pulseWidth = (60000000/RPM)/360; // time for 1° in uS
      barWidth = pulseWidth * widthValue; //result is in uS based on width of bar in degrees
     // RPM = 500;//for my serial test purposes 500-3800
    }

    timerTopValue = 15000000UL / RPM;
    OCR1B = barWidth;

    if (digitalRead(setFallingSwitch) == LOW) //set Falling
    {
      risefall = 'F';
    }
    else
    {
      risefall = 'R';
    }

    lcd.setCursor(6, 0);
    lcd.print("     "); // print blank spaces to clear old data
    lcd.setCursor(6, 0);
    lcd.print(RPM);

    lcd.setCursor(18, 0);
    lcd.print("  ");
    lcd.setCursor(18, 0);
    lcd.print(risefall); //print R or F at upper right side

    lcd.setCursor(6, 1);
    lcd.print("   ");
    lcd.setCursor(6, 1);
    lcd.print(pickup);

    lcd.setCursor(16, 1);
    lcd.print("   ");
    lcd.setCursor(16, 1);
    lcd.print(pulseWidth);

    lcd.setCursor(11, 2);
    lcd.print("       ");
    lcd.setCursor(11, 2);
    lcd.print(copy_delayPeriod);
   
    lcd.setCursor(14, 3);
    lcd.print("      ");
    lcd.setCursor(14, 3);
    lcd.print(delayDegrees, 1);   //delayDegrees, 1);
  }
 
  if (trigger == true && interruptFlag == true )
  {
    trigger = false;
    interruptFlag = false;
    noInterrupts();
    copy_delayPeriod = delayPeriod;
    interrupts();

    delayDegrees = pickup - (360.0 * (copy_delayPeriod) / (timerTopValue * 4.0)); //for decimal place in deg display.
    }
}

ISR(TIMER1_COMPA_vect) {
  OCR1A = (timerTopValue); // value to set delay between pulses to trigger cdi
  digitalWrite(chargePin, LOW); //guarantee off charge pin at trigger
  digitalWrite(outputPin, HIGH); //turn on pin  trigger

  if (risefall == 'R') //switch not set; default Rising trigger
  {
    delayPeriodStart = micros(); //start looking for response as pulse rises
    trigger = true;
  }
  //start Timer 2 for charge pulses
  if (chargePulse)
  {
    timeSliceCount = 0;
    TCNT2 = 0;
    OCR2A = timerTopValue/96; //set 12 periods
    TCCR2B |=  1 << CS22 | 1 << CS21; //prescaleer 256 16us/tick
  }
}

ISR(TIMER1_COMPB_vect) {
  digitalWrite(outputPin, LOW);

  if (risefall == 'F') //switch set for Falling trigger
  {
    delayPeriodStart = micros(); //start looking for response as pulse falls
    trigger = true;
  }
}

void delayPeriodTiming()
{
  delayPeriod = micros() - delayPeriodStart;
  interruptFlag = true;
}

ISR(TIMER2_OVF_vect)
//5 pulses of 30 degrees starting at 60 degrees
//ON at 60,120,180,240,300 = 2,4,6,8,10
//OFF at 90,150,210,270,330 = 3,5,7,9,11
{
  if (timeSliceCount != 0 && timeSliceCount % 2 == 0)
  {
    digitalWrite (chargePin, HIGH);
  }
  else //if (timeSliceCount == 0 || timeSliceCount % 2 == 1)
  {
    digitalWrite(chargePin, LOW);
  }
  timeSliceCount++;

  if (timeSliceCount == 12)
  {
    timeSliceCount = 0;
    //stop Timer2 by clearing prescaler bits
    TCCR2B &= ~1<< CS22;
    TCCR2B &= ~1<< CS21;
  }
}

You replied while I was typing this so we can both take a look again tomorrow.

Tom--

You are close, but need to account for the Timer prescaler. The timer setup uses a prescaler of 64, so that every "tick" takes 4 microseconds. That's why the OCR1B value of 125 give .5 millisecond (500 microseconds)for the pulse width.

Let's walk through the math.

You are correct, that you use 1/RPM to work with time.

Minutes_per_Revolution = 1/Revolutions_per_Minutes
Microseconds_per_revolution = 6010001000/Revolutions_per_Minute = 60000000/Revolutions_per_Minute
There are 360 Degrees_per_Revolution so Microseconds_per_degree= 60000000/360*Revolutions_per_Minute
Since every Tick takes 4 microseconds we need to divide by 4 to get Ticks_per_Revolution
Ticks_per_Degree = 60000000/(4 * 360 * Revolutions_per_Minute) = 41667/Revolutions_per_Minute

With your variable int potWidth = analogRead(pot3) and the range of lengths of the pulse generating bar 2 - 32 degrees you could use
Width = map(potWidth, 0, 1023, 2, 32); You know better than I what the map should be.

However, to keep things simple and fast I would recommend using Width = potWidth/32 which will be a very fast bit shift operation to the compiler and will cover the range from 1 to 31 degrees

Then you will set OCR1B = Width*Ticks_per_Degree

VariableTriggerPulse.ino (8.01 KB)

Hi, Seems to work, until I get pot installed for potWidth how can I write a value in the code? For example if the value is 23 degrees? I tried Width = 23/32; //potWidth/32;

Hi, Seems to work, until I get pot installed for potWidth how can I write a value in the code? For example if the value is 23 degrees?

I'm glad to hear things look good.

Width = 23;

I like some of the clean up and variable renaming in the code you posted last night. You should incorporate it into the code I attached.

Ok, when not using map() what does the analogRead(pot3) return? 0 to 1023 or 0 to 255?

analogRead(pot3) returns 0-1023. You then divide that value by 32 to scale it from 0 - 31 for a degrees value.

OK that's what I thought, so it could be changed from 32 to 25 for 0 to 41 if needed? Not going to just for learning!! I have a spreadsheet that is used for programming some CDI's I make and it gives all sorts of values based on several settings so it is handy for confirming that the readings of the tester are accurate. Looks like everything is right on.
Thanks again,
Tom