DC motor encoder using IR sensor and interrupt code.

Trying to control the speed of a DC motor with an IR sensor, I had it working before using interrupt but was told interrupt would be more precise. so I added interrupt into my code. I am using a L298N motor driver that i send PWM to.

When i run the code the program just sends the largest pwm signal it can, i monitor the pwm send and it constantly reads 255 (max allowable).

#include <LiquidCrystal.h>

LiquidCrystal lcd(8,9,4,5,6,7);

volatile unsigned long RPMN1count=0;
volatile unsigned long RPMN1=0;
volatile boolean flag=0;
unsigned long lcdrefresh;

const int IRstate = 2; 
const int motor = 11; 
int forward = 12; // motor direction on motor driver forward
int reverse = 13; // motor direction on motor driver reverse
const int accuracy = 5; // Accuracy of output
const int N = 3; // No. of references per turn
int pwm = 0;
int setrpm;
int rpm; // RPM value

void setup()
{
  attachInterrupt(digitalPinToInterrupt(IRstate), N1, RISING);
  lcd.begin(16,2);
  pinMode(IRstate,INPUT);
  pinMode(12,OUTPUT);
  pinMode(13,OUTPUT);
  Serial.begin(9600);

}

void loop()
{
  // put your main code here, to run repeatedly: 
 if(flag=1){
    
    noInterrupts (); 
    RPMN1=(RPMN1count-micros())*N;
    RPMN1count=micros();
    rpm=(60000000/RPMN1);
    interrupts ();
    flag=0;
  
  setrpm = map(analogRead(A5), 0, 1023, 0, 5000); // Read voltage from potentiometer from analog pin 5 to define and scale setrpm.
                                    
  // Change PWM accordingly
  if( rpm > (setrpm*(1+(acc/100))) )   // If current RPM > required
   {
    pwm = pwm-1;     // Decrease PWM
    if(pwm = -1) {pwm = 0;}
    digitalWrite(forward,LOW);
    digitalWrite(reverse,HIGH);
    analogWrite(motor,pwm); 
   }
  
  if( rpm < (setrpm*(1-(acc/100))) )   // If current RPM < required
   {
    pwm = pwm+1;     // Increase PWM
    if(pwm = 256) { pwm = 255; }
    digitalWrite(forward,LOW);
    digitalWrite(reverse,HIGH);
    analogWrite(motor,pwm); 
   } 
 }
   Serial.println(pwm); // Print pwm send to monitor

  // LCD Display
  if( ( millis()-lcdrefresh ) >= 100 )
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Set RPM = ");
      lcd.print(setrpm);
      lcd.setCursor(0,1);
      lcd.print("RPM = ");
      lcd.print(rpm); 
      if(pwm == 255 )
       { lcd.print(" Max!"); }  // If PWM = 255 ie. max value. Speed will not increase beyond that
      lcdrefresh = millis();   
    } 
  
}


void N1()
{
    RPMN1=(RPMN1count-micros())*N;
    RPMN1count=micros();
    flag=1;

}

interruptsketch.ino (2.17 KB)

 if(flag=1){
   
    noInterrupts ();
    RPMN1=(RPMN1count-micros())*N;
    RPMN1count=micros();
    rpm=(60000000/RPMN1);
    interrupts ();
    flag=0;

Why do you duplicate your interrupt handler code in the loop? You calculate RPMN1 in the interrupt handler, so no need to do it here again and setting RPMN1count to the value of micros() ruins the complete time measurment. So that code should read

 if(flag=1){
   
    noInterrupts ();
    rpm=(60000000/RPMN1);
    interrupts ();
    flag=0;

given your RPM calculation is correct (I cannot check that because you didn't describe your hardware setup).

The rpm variable probably must be of uint32_t.

BTW, the code doesn't compile (acc variable is not defined). If that should read "accuracy" (which is defined), your calculation is wrong (5/100 is always 0, remember these are integers).

That code also does an assignment in the if() statement, not a compare

 if(flag=1)

vs.

 if(flag==1)

There will be many fewer calculations (and therefore more time for the Arduino to be useful) if you control the speed based on the number of microseconds per revolution (or µsecs between pulses as appropriate) rather than calculating RPM each time.

This link has code that is derived from a motor control program I wrote for a small DC motor with one pulse per revolution.

...R