2 channel encoder simulator

Hi All.
I amm trying to create quadrature encoder simulator.
While getting it done with delayMicrosecods() seems to be ok, then repurposing millis() to do the same thing do not seems to work at all.
Can someone suggest what I`m missing in this code?
This should in theory produce 90deg separate square waves.

Thanks for any help.

const int PhaseA = 3;
const int PhaseB = 4;

       

unsigned long previousMicrosPhaseA = 0;     
unsigned long previousMicrosPhaseB = 0;     


const long interval = 300;           

void setup() {
  
  pinMode(PhaseA, OUTPUT);
  pinMode(PhaseB, OUTPUT);
}

void loop() {

  unsigned long currentMicrosPhaseA = micros();
  unsigned long currentMicrosPhaseB = micros();

  if (currentMicrosPhaseA - previousMicrosPhaseA >= interval) {
    digitalWrite(PhaseA, HIGH);
       previousMicrosPhaseA = currentMicrosPhaseA; 
        
  }

    if (currentMicrosPhaseB - previousMicrosPhaseB >= interval) {
      digitalWrite(PhaseB, LOW);
       previousMicrosPhaseB = currentMicrosPhaseB; 
        
  }

    if (currentMicrosPhaseA - previousMicrosPhaseA >= interval) {
       previousMicrosPhaseA = currentMicrosPhaseA; 
    digitalWrite(PhaseA, LOW);  
  }
 
   if (currentMicrosPhaseB - previousMicrosPhaseB >= interval) {
       previousMicrosPhaseB = currentMicrosPhaseB; 
    digitalWrite(PhaseB, HIGH);
  }
}

I see no reference to either delayMicroseconds() or millis() in your code. Please post the code that

and describe the problem in detail

How do these get out of phase WRT each other?

It would be easier and more clear to just run a 4 state switch/case mechanism at the appropriate frequency.

a7

Hi Guys
Sorry
Reference to the delayMicroseconds();
On the scope these are set 90deg apart when run

int time = 0;
int microsec = 0;

void setup() {

  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(A0, INPUT);
  Serial.begin(9600);

}


void loop() {
  time = analogRead(A0);
  microsec = map(time, 0, 1023, 0, 120);
  digitalWrite(3, HIGH);   
  delayMicroseconds(microsec);  
  digitalWrite(4, LOW);    
  delayMicroseconds(microsec);                    
  digitalWrite(3, LOW);    
  delayMicroseconds(microsec);      
   digitalWrite(4, HIGH);   
   delayMicroseconds(microsec);            
   //Serial.println(microsec);
}

@alto777 What i was hoping to get using these 2 are to write phaseA LOW, then delay with the set time, then write phaseB and so on. I have no clue how to tackle that without stopping the processor.

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

You can use micros() in the same way as millis()

Stop thinking about it in terms of delay, or delay().

Look into FSM or finite state machines. This is a very simple matter.

By using a 4 state switch/case mechanism, you would only have to code one millis() based timer, which would look like your code only just one frequency, as you might see in BOD blink without delay or any number of examples.

Someone will be along soon to just write the code for you. Don't let that happen, you can do this. :expressionless:

a7

@UKHeliBob
Ok, so i can do that which is not a problem, but this is not shifting the phase of desired degree

const int PhaseA = 3;
const int PhaseB = 4;

int PhaseAState = LOW; 
int PhaseBState = HIGH;
unsigned long previousMicrosPhaseA = 0;     
unsigned long previousMicrosPhaseB = 0;     


const long interval = 300;           

void setup() {
  
  pinMode(PhaseA, OUTPUT);
  pinMode(PhaseB, OUTPUT);
}

void loop() {

  unsigned long currentMicrosPhaseA = micros();
//  unsigned long currentMicrosPhaseB = micros();

  if (currentMicrosPhaseA - previousMicrosPhaseA >= interval) {
   
       previousMicrosPhaseA = currentMicrosPhaseA; 
           if (PhaseAState == LOW)
      PhaseAState = HIGH;
    else
      PhaseAState = LOW;
   
      if (PhaseBState == LOW)
      PhaseBState = HIGH;
      else
      PhaseBState = LOW;

        digitalWrite(PhaseA, PhaseAState);
        digitalWrite(PhaseB, PhaseBState);
        
  }
}

@alto777
Ok, I`ll give it a go.
Is it that what you refering to?

switch (var) {
  case label1:
    // statements
    break;
  case label2:
    // statements
    break;
  default:
    // statements
    break;
}

Yes yes.

Four cases, each case writes both digital pins as required to achieve the Quadra true <-- haha quadrature phase for that step, like you delay-based code HH HL LL LH if I am awake.

Inside a millis based timing mechanism.

You can even get this to work with delay() as a first version.

Switch/case to set the pins. A counter that runs 0, 1, 2, 3, 0 or that same counter modulo 4 (% modulo operator) selects the case.

Then delay(xx), the time for one phase.

Either approach just plopped into you loop(), which will do its job and, well, loop over that.

At the top of the loop, interrogate any input that changes the frequency as it appears you doing with analaogRead().

a7

Yes switch case can be used as a statemachine

case 1:
switch Ch A high

case 2:
switch Ch A low CH B High

case 3:
switch Ch B low

case 4:
additional waiting time with Ch A and Ch B low
reset state-variable to 1

best regards Stefan

What? No. The timing is separate from the four states. You've made it more complicated.

I do see, however, that each case doesn't technically have to write both pins, as only one changes from state to state.

But the cost of the extra digitakWrites() is more than returned by the clarity of what each case does or means.

The timing can be handled without (outside of) the switch/case.

a7

Does it need to cope with both directions?

This little demo of mine does.

p.s. I used the special current limiting LEDs. :wink:

@wildbill it will need eventually

Undoubtedly one of wokwi.com’s better features!

With the state machine, just decrementing the counter should make the encoder simulation go… backwards.

a7

Using different previousMillis values for timing the two phases is a truly terrible idea. There is nothing to prevent them getting out of phase due to accumulated timing errors. Use a SINGLE previousMillis value to generate BOTH.

Works, but is unnecessary complicated. One clock marching through four states is obvoiuser at a glance.

But what did I tell ya… someone has went and written the code for you.

a7

You should be able to see how to change this for microseconds and your minimum and maximum period.

void setup() {

  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);

  pinMode(A0, INPUT);

  Serial.begin(9600);

}

void loop()
{
  static unsigned long lastTime = 0;
  static unsigned char phase = 0;

  unsigned long now = millis();

  int period = analogRead(A0);
  period = map(period, 0, 1023, 83, 750);

  if (now - lastTime < period) return;
  lastTime = now;

//  Serial.println(analogRead(A0));

  switch (phase & 0x3) {    // just use bottom two bits of the phase counter
  case 0 :
    digitalWrite(3, LOW);   
    digitalWrite(4, LOW);    
    break;

  case 1 :
    digitalWrite(3, LOW);   
    digitalWrite(4, HIGH);    
    break;

  case 2 :
    digitalWrite(3, HIGH);   
    digitalWrite(4, HIGH);    
    break;

  case 3 :
    digitalWrite(3, HIGH);   
    digitalWrite(4, LOW);    
    break;
  }

  phase++;
}

Of course I did:

HTH

a7

@alto777 thanks buddy. I did not had a chance to try it out anything yet. Will update you all on Monday with the final product (code).