Control Variable Error in Program while using PulseIn/Interrupts/Serial.Print

I am trying to build a circuit that reads in a PWM signal from a RC remote then controls the position of a DC motor with a quadrature encoder.

I am building my code off of a tutorial by www.HomoFaciens.de.

The circuit is wired up as shown (plus a variable resistor going to pin A1 and a PWM signal going to pin 8).

I modified the original code to run off input from an analog input from a variable resistor to try and get it working. Using analogRead the entire circuit works great.

However, when i try to read in a PWM pulse from an RC receiver (using either PulseIn or using interrupts), I get some strange behavior. My "actualPoint" variable starts acting all crazy, even though the PWM signal is being read correctly (or so I think) another strange behavior of this code is that if Serial.println() is used, seemingly at all, my actualPoint variable gets broken.

Code is as follows:

//Digital wiper motor servo
//Source & info at: www.HomoFaciens.de/technics-base-circuits-encoder-disc_en_navion.htm

#include <util/delay.h>

int stepStatus = 0;
int sensorStatusA = 0;
int sensorStatusB = 0;
int count = 0;
int stepDone = 0;
int diffTime = 0;
signed long PotPosition = 0;
signed long prevPot = 0;
unsigned long millisStart = 0;
unsigned long millisNow = 0;
unsigned long millisDiff = 0;
int dutyCycle = 0;
signed long setPoint = 0;
signed long actualPoint = 0;
byte readByte = 0;

#define ENABLE_R 10
#define ENABLE_L 9

#define PWM_IN 8

#define SENSOR_A 3
#define SENSOR_B 4
#define ANALOG_IN A1

#define MOTOR_DIRECTION 5
#define MOTOR_PWM 6

#define LED 13             //Status LED of servo

#define P_FRACTION 1     //Proportional factor of control loop 0.001 - 10.0 (1.0)
#define I_FRACTION 0.3     //Integral factor of control loop 0.0 - 10.0 (0.3)
#define SOFT_START 0.0005  //0.0 - 1.0 (0.0005)
#define STEP_MARGIN 22     //10 - 1000 (1)

#define MIN_DUTYCYCLE 125   //0 - 255 (125)
#define MAX_DUTYCYCLE 255  //0 - 255 (255)


void setup() {
  //Serial.begin(9600); 
  
  pinMode(SENSOR_A, INPUT);
  pinMode(SENSOR_B, INPUT);
  pinMode(ANALOG_IN, INPUT);
  pinMode(PWM_IN, INPUT);  
  pinMode(LED, OUTPUT);   
  pinMode(MOTOR_DIRECTION, OUTPUT);  
  pinMode(MOTOR_PWM, OUTPUT);    
  
  
  analogWrite(MOTOR_PWM, 0);
  digitalWrite(LED, 1);
  count = 0;
}


void loop() {    
      
      PotPosition = pulseIn(PWM_IN, HIGH, 25000); // FirePin
      //PotPosition = analogRead(ANALOG_IN);
      if ((PotPosition > prevPot+5) || (PotPosition < prevPot-5)) {
        //setPoint = map(PotPosition, 0, 1023, 0, 36*22*2);
        setPoint = map(PotPosition, 980, 1980, 0, 36*22*2);
        prevPot = PotPosition;
      }
    
      sensorStatusA = digitalRead(SENSOR_A);
      sensorStatusB = digitalRead(SENSOR_B);
      
      diffTime++;
      
      if(sensorStatusB == 0 && sensorStatusA == 0){
        if(stepStatus == 1){
          actualPoint--;
          diffTime = 0;
        }
        if(stepStatus == 3){
          actualPoint++;
          diffTime = 0;
        }
        stepStatus = 0;
      }
    
      if(sensorStatusB == 1 && sensorStatusA == 0){
        if(stepStatus == 0){
          actualPoint++;
          diffTime = 0;
        }
        if(stepStatus == 2){
          actualPoint--;
          diffTime = 0;
        }
        stepStatus = 1;
      }
    
      if(sensorStatusB == 1 && sensorStatusA == 1){
        if(stepStatus == 3){
          actualPoint--;
          diffTime = 0;
        }
        if(stepStatus == 1){
          actualPoint++;
          diffTime = 0;
        }
        stepStatus = 2;
      }
    
      if(sensorStatusB == 0 && sensorStatusA == 1){
        if(stepStatus == 2){
          actualPoint++;
          diffTime = 0;
        }
        if(stepStatus == 0){
          actualPoint--;
          diffTime = 0;
        }
        stepStatus = 3;
      }

      dutyCycle = (double)(abs(setPoint - actualPoint)) * (double)P_FRACTION;
      dutyCycle += (double)(diffTime * I_FRACTION);

      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }

      if(SOFT_START * (double)(millisNow - millisStart) < 1.0){
        dutyCycle = (double)(dutyCycle * SOFT_START * (millisNow - millisStart));
      }
      
      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }
      if(abs(setPoint - actualPoint) < STEP_MARGIN){
        stepDone = 1;
        diffTime = 0;
        analogWrite(MOTOR_PWM, 0);
        digitalWrite(MOTOR_DIRECTION, 0);
        digitalWrite(ENABLE_R, 0);
        digitalWrite(ENABLE_L, 0);
      }
      else{
        digitalWrite(ENABLE_R, 1);
        digitalWrite(ENABLE_L, 1);
        if(actualPoint < setPoint){
          digitalWrite(MOTOR_DIRECTION, 1);
          analogWrite(MOTOR_PWM, 255 - dutyCycle);
        }
        if(actualPoint > setPoint){
          digitalWrite(MOTOR_DIRECTION, 0);
          analogWrite(MOTOR_PWM, dutyCycle);
        }
      }
         
    millisNow = millis();
    if(millisStart > millisNow){
      millisStart = millisNow;
    }
  
}

Anyone have any ideas? Maybe there is a better way to do this that I didn't find yet.

digital-servo-L298N.pdf (700 KB)

The loop() is very time critical because it contains code to read optical sensors which are triggered multiple times per motor revolution. Function pulseIn() blocks the loop while it waits for a pulse. Even serialPrint() at least at 9600 baud is apparently too slow.

Edit
Post the code where you attempted to use an interrupt to read the RC receiver and say what the frequency of the pwm signal is. An interrupt should work if there is not too much time consuming code in the service routine.

Gah! You were correct!

An interrupt works great, my problem was that in the interrupt code I was still calling serial functions (which apparently take too much time).

Anyway, here is working code for future generations:

//Digital wiper motor servo
//Source & info at: www.HomoFaciens.de/technics-base-circuits-encoder-disc_en_navion.htm

#include <util/delay.h>
#include <PinChangeInterrupt.h>

const byte channel_pin[] = {8};
volatile unsigned long rising_start[] = {0};
volatile long channel_length[] = {0};

int stepStatus = 0;
int sensorStatusA = 0;
int sensorStatusB = 0;
int count = 0;
int stepDone = 0;
int diffTime = 0;
signed long PotPosition = 0;
unsigned long millisStart = 0;
unsigned long millisNow = 0;
unsigned long millisDiff = 0;
int dutyCycle = 0;
signed long setPoint = 0;
signed long actualPoint = 0;
byte readByte = 0;

#define ENABLE_R 11
#define ENABLE_L 9

#define SENSOR_A 3
#define SENSOR_B 4
#define ANALOG_IN A1

#define MOTOR_DIRECTION 5
#define MOTOR_PWM 6

#define LED 13             //Status LED of servo

#define P_FRACTION 1.0     //Proportional factor of control loop 0.001 - 10.0 (1.0)
#define I_FRACTION 0.3     //Integral factor of control loop 0.0 - 10.0 (0.3)
#define SOFT_START 0.0005  //0.0 - 1.0 (0.0005)
#define STEP_MARGIN 10*3     //10 - 1000 (1)

#define MIN_DUTYCYCLE 150   //0 - 255 (125)
#define MAX_DUTYCYCLE 255  //0 - 255 (255)


void setup() {


  pinMode(channel_pin[0], INPUT);
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(channel_pin[0]), onRising0, CHANGE);
    
  pinMode(SENSOR_A, INPUT);
  pinMode(SENSOR_B, INPUT);
  pinMode(ANALOG_IN, INPUT);  
  pinMode(LED, OUTPUT);   
  pinMode(MOTOR_DIRECTION, OUTPUT);  
  pinMode(MOTOR_PWM, OUTPUT);    
  //Serial.begin(9600); 
  
  analogWrite(MOTOR_PWM, 0);
  digitalWrite(LED, 1);
  count = 0;
}

void processPin(byte pin) {
  uint8_t trigger = getPinChangeInterruptTrigger(digitalPinToPCINT(channel_pin[pin]));

  if(trigger == RISING) {
    rising_start[pin] = micros();
  } else if(trigger == FALLING) {
    channel_length[pin] = micros() - rising_start[pin];
  }
}

void onRising0(void) {
  processPin(0);
}

void loop() {    
      
      PotPosition = channel_length[0];
      //PotPosition = analogRead(ANALOG_IN);
      if ((PotPosition > setPoint+3) || (PotPosition < setPoint-3)) {
        //setPoint = map(PotPosition, 0, 1023, 0, 3600);
        setPoint = map(PotPosition, 980, 1980, 0, 36*44*3);
      }
      
      sensorStatusA = digitalRead(SENSOR_A);
      sensorStatusB = digitalRead(SENSOR_B);
      
      diffTime++;
      
      if(sensorStatusB == 0 && sensorStatusA == 0){
        if(stepStatus == 1){
          actualPoint--;
          diffTime = 0;
        }
        if(stepStatus == 3){
          actualPoint++;
          diffTime = 0;
        }
        stepStatus = 0;
      }
    
      if(sensorStatusB == 1 && sensorStatusA == 0){
        if(stepStatus == 0){
          actualPoint++;
          diffTime = 0;
        }
        if(stepStatus == 2){
          actualPoint--;
          diffTime = 0;
        }
        stepStatus = 1;
      }
    
      if(sensorStatusB == 1 && sensorStatusA == 1){
        if(stepStatus == 3){
          actualPoint--;
          diffTime = 0;
        }
        if(stepStatus == 1){
          actualPoint++;
          diffTime = 0;
        }
        stepStatus = 2;
      }
    
      if(sensorStatusB == 0 && sensorStatusA == 1){
        if(stepStatus == 2){
          actualPoint++;
          diffTime = 0;
        }
        if(stepStatus == 0){
          actualPoint--;
          diffTime = 0;
        }
        stepStatus = 3;
      }

      dutyCycle = (double)(abs(setPoint - actualPoint)) * (double)P_FRACTION;
      dutyCycle += (double)(diffTime * I_FRACTION);

      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }

      if(SOFT_START * (double)(millisNow - millisStart) < 1.0){
        dutyCycle = (double)(dutyCycle * SOFT_START * (millisNow - millisStart));
      }
      
      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }
      if(abs(setPoint - actualPoint) < STEP_MARGIN){
        stepDone = 1;
        diffTime = 0;
        analogWrite(MOTOR_PWM, 0);
        digitalWrite(MOTOR_DIRECTION, 0);
        digitalWrite(ENABLE_R, 0);
        digitalWrite(ENABLE_L, 0);
      }
      else{
        digitalWrite(ENABLE_R, 1);
        digitalWrite(ENABLE_L, 1);
        if(actualPoint < setPoint){
          digitalWrite(MOTOR_DIRECTION, 1);
          analogWrite(MOTOR_PWM, 255 - dutyCycle);
        }
        if(actualPoint > setPoint){
          digitalWrite(MOTOR_DIRECTION, 0);
          analogWrite(MOTOR_PWM, dutyCycle);
        }
      }
          
    millisNow = millis();
    if(millisStart > millisNow){
      millisStart = millisNow;
    }
  
}

Thanks again!

If anyone has any suggestions to make this code better, let me know!