PWM & encoder interrupt

Hello
I'm attaching encoders to robotic arm. First thing I did was to build and test encoder itself. It's pretty standard and circuit is working excellent with interrupts. Nice resolution, no pulses missing. BUT - when I continued my experiments attaching motor control code I wrote & tested before I noticed that encoder resolution dropped dramatically - it was still correctly counting pulses but Serial.print() them sporadically & analysed in code with delay. Commenting out line by line I discovered that if analogWrite() is used along with Interrupts then this happens.

Task to report pulse # 10
Without PWM - 1,2,3,4,5,6,7,8,9,10 - done (in 5 seconds)
With PWM 1,5,7 - done (in 1 second)

  • Is it because of analogWrite() PWM uses timers messing with Interrupts?
  • How to use PWM for motor speed control & still use interrupt for encoder? Is it possible on single Arduino?
  • I'm going to control 5 joints (motors) with 5 encoders and 5 limit sensors. What is the best design approach? Single chip? Multiple chips on 1Wire LAN separating motor / encoder control and master chip?
  • Do you know any resources on internet about how similar tasks are handled in industrial robots?

Thank you in advance

Janis

Hi Janis,

If you using the analogWrite in the IRQ routine itself I understand the behaviour. Interrupts should be as smal as possible otherwise they are not handled when they are called again, resulting in fatal errors. Just keep the countingin the IRQ and do the processing in the main loop().

Can you publish the code you have so far? That makes it much easier to help

If you are building a 5 axis robot you'll need far more computing power than an arduino to manage trajectory control, etc. I would suggest EMC2 which is ideally suited for your application and will run on a small PC like the Atom ITX . Check it out here: http://www.linuxcnc.org/ If you search the several forums available you will find examples of other multi-axis robots.

@ robtillaart: I'm sorry for delay, code is here. It is quite incomplete as I had to stop when encountered interrupt problem. The code purpose is to receive serial command, decode it and enable motor until encoder registers sufficient interrupts. I'm afraid that polling is the only option :frowning:

@ Yankee: I know about limitations and have no intention to control 5 motors at once. My idea is to build network of controllers each one driving one motor, one encoder and two hall effect motor limit sensors. Does this makes sense?

// define pins
int ma = 7; 		// L293D dir pin A
int mb = 8; 		// L293D dir pin B
int mpulse = 6; 	// ARDUINIO PWM pin
int mencoder = 2;	// Arduino interupt ping for encoder


int rotationDirection = 0;	// initially rotation command is set to stop
int motorSpeed = 0;		// initially motor speed - pwm duty syscle is 0


// message format [+/- direction][number_of_encoder_ticks][ascii 13]
int messageCounter = 0;		
int messageValue = 0;
int inByte[5];			// array for icomming command bytes
int mul = 1;			// multiplyer for string to int conversion

boolean doCmd = false;		// correct message received, execute motor cmd

volatile unsigned int encoderValue = 0;	// Encoder value from interrupt routine
int oldEncoderValue = 0;		// Encoder value from interrupt routine  ???????

// define constants
const int stp = 0;	// motor stop
const int fwd = 1;	// forward
const int bkw = 2;	// backward

const int maxMsgLenght = 3;	// maximum length of command

void setup() {
  Serial.begin(9600);
  Serial.println("Motor-encoder test console, enter number of encoder clicks"); 
  pinMode(ma, OUTPUT);
  pinMode(mb, OUTPUT);
  pinMode(mpulse, OUTPUT);
  pinMode(mencoder, INPUT);
  attachInterrupt(0, countEncoder, CHANGE); 
  analogWrite(mpulse, 0); // if this enabled them encoder pulses are skipped
}

void loop() {
  if (doCmd == false) {
    if (Serial.available() > 0) {
      inByte[messageCounter] = Serial.read();

      // Set motor direction --------------------------------------------
      if (inByte[messageCounter] == 43) {
        rotationDirection = fwd;
        messageCounter = 0; 		// receive firts byte, set direction, and reset cycle to receive integer
        digitalWrite(ma, HIGH);
        digitalWrite(mb, LOW);
        Serial.print("Move + "); 
      }

      // Set motor direction --------------------------------------------
      if (inByte[messageCounter] == 45) {
        rotationDirection = bkw;
        messageCounter = 0;   		// receive firts byte, set direction, and reset cycle to receive integer
        digitalWrite(ma, LOW);
        digitalWrite(mb, HIGH);
        Serial.print("Move - "); 
      }

      // receive digits --------------------------------------------
      if ((inByte[messageCounter] >= 48) && (inByte[messageCounter]  <= 57)){
        messageCounter++;		// if we are receiving ascii bytes for integers just increment counter
      }

      // decode & execute message --------------------------------------------
      if (inByte[messageCounter] == 13)  {
        inByte[messageCounter] = 0;	// clear ascii 13 fro array
        mul = 1;
        Serial.flush();
        for (int x = messageCounter; x > 0 ; x--) {     
          messageValue = messageValue +  ((inByte[x-1]-48) * mul) ; 
          mul = mul * 10;
        }
        messageCounter = 0;
        encoderValue = 0;
        Serial.println(messageValue);
        interrupts();			// after command value is calculated we enable interrupts	
        analogWrite(mpulse, 250);    // if this enabled them encoder pulses are skipped     
        doCmd = true;			// and enable motor loop flag
      }
    }
  }

  // doing command  - motor routine loop -------------------------------------------
  if (doCmd == true) {
    if (encoderValue != oldEncoderValue){
      oldEncoderValue = encoderValue;
      Serial.print(encoderValue);
      Serial.print(",");  
  }

    if (encoderValue >= messageValue){ //when pulse count is ok disable pwm and motor
      analogWrite(mpulse, 0);
      digitalWrite(ma, LOW);
      digitalWrite(mb, LOW);
      Serial.println("\nDone");
      doCmd = false;
      encoderValue=0;
      messageValue=0;
    }
  }


} // -------- end of lopp

void countEncoder(){
  encoderValue=encoderValue+1;
}

Well, perhaps it makes sense as an intellectual exercise but to accomplish useful tasks a robotic arm needs coordinated motion between the joints. Not trying to discourage your efforts, just pointing out that you will be "reinventing the wheel" with this approach. Having said that, I would love to see more details of what you are doing. Have any pics of your setup?

Hello Yankee

By inventing the wheel do you mean CAN?
I'm ultimately interested in controlling self contained, PC-less multi-motor kinetic puzzles . There network of independent slave controllers can provide coordinated movement of multiple motors. I imagine sending single text string with commands for multiple motors at once. Like "m1f50-m2r30-m3f40". What do you think about that?
Also do you have any advice about choosing micro controller for this - I need to control one motor, poll three sensors & receive serial data.