Get too much noise from reading an encoder

I'm working on a project requiring generating PWM for a ESC+ brushless DC motor and measuring speed from an encoder at the same time. I run the motor at rather high speed, appox. 9000RPM. The big problem is that the data i get from the encoder has too much unwanted noise. =(

I access Timer 1 directly to generate PWM on pin9. A part of my code is aimed to vary the PWM around a trim point. Please don't let you be confused with that one. It's just a part of my project requirements, and it has nothing to worry about. :stuck_out_tongue:

To read the encoder, I use a pair of photocouples with one pin being attached to pin2 , to make an interrupt signal. micros() function is called to measure the time between every 3 successive rising edges being noticed at pin2.

I was afraid the interrupts messing up with each other, which result in those really sharp noise. But I can't figure out the reason why! :cold_sweat:

Hope someone can help! I tried to write comments as much as possible, but may not be able to avoid confusion. Sorry for i'm not a good programmer, just an aerospace student with weak background in both programming skills and electronics knowledge. But this code is a really important part in my project. I need this code to run my experiment.

Thanks for your help!

/***********************************************
This code measures angular rate response of step
input (pulse width), which is varied
periodically about trim (hovering) point. 

OUTPUT: Period in every successive N cycles 
       (Unit: us)
***********************************************/
#include <avr/interrupt.h>
#include <Arduino.h> 

#define OUTPUT_PIN 9// PWM pin (timer1)
#define PWM_PERIOD 20000//microsecond
#define PRESCALER 8
#define ENCODER_IN_PIN 2//PIN number of INT0 on the Arduino UNO
#define ENCODER_INT 0// INTO
#define NUMBER_OF_CYCLES 3// The value is updated after every ~ pulses 
#define TRIM_POINT 2200//pulse width at hovering condition (from 2000 to 4000)
#define UNIT_ANGLE 36 //swept angle correspoding to 2 successive rising pulses (degree)
#define INTERVAL 3000//step signal duration (milisec)
#define DELTA 0// value of PWM around trimpoint 

int outputPin = OUTPUT_PIN;
int inputPin = A0;
int i = 0;
int delta = DELTA;
double velocity;
unsigned int temp;
unsigned int time[500];// logging data into an array with 500 values
int a = 0;//array index var

volatile int x = 0;
volatile boolean NewSignal = true;
volatile boolean New_status = true;
volatile unsigned long  StartPeriod;
volatile unsigned long  StartInterval;
volatile unsigned long  Period = 0;


void setup(){
  attachInterrupt ( ENCODER_INT, calc, RISING );
  //digitalWrite(ENCODER_IN_PIN, HIGH);
  initPin (outputPin);
   /*set prescaler=8, 0.5us/ticks => timeperiod=20ms, freq=50
    mode14: FAST PWM, TOP= ICR1*/
  TCCR1A = (1<<COM1A1)|(1<<WGM11);
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);
  ICR1   = 40000; //set TOP value (ticks)
  OCR1A  = 2000;
  Serial.begin(38400);
  delay(3000);
  OCR1A  = TRIM_POINT;

}
void loop(){
   
   temp = millis()-StartInterval;
   //Serial.println(temp); //for debug purpose 
     
// need two variables, one's to allow velocity to be updated(fast). Another to allow the OCR1A to vary!(slow)

   if (temp >= INTERVAL){ //When an interval has been expired, change OCR1A value
      if ((i%2) >0){
      delta=(~delta) + 0x01;
      }
      OCR1A = OCR1A + delta;
      StartInterval = millis();
      i++;
      //Serial.println(delta); //for debug purpose
    
   }
 
   while (New_status && NewSignal){ //Allow updating! 
      time[a] = Period;
      a++;
      New_status = false;
   }
   // after having recorded 500 values, stop the motor and send off the data.
   if (a == 499){ 
     for (a=0;a<500;a++){
       Serial.println(time[a]);
     }
     OCR1A=2000;//stop the motor
   }
   
}
//procedure to start the electronic speed controller  
void initPin(int pin_number){
  digitalWrite(pin_number, HIGH);
  pinMode(pin_number, OUTPUT);
  digitalWrite(pin_number, HIGH);
}

//calc calculates & returns the period after 
//every NUMBER_OF_CYCLES successive cycles of Pulse.  
void calc(){
  
  if (NewSignal && !New_status){ //start new N cycles
  StartPeriod = micros();
  NewSignal = false;//to lock the main loop from updating while calculting new period is in process.<<<<<<
  }
  
  if (x==NUMBER_OF_CYCLES){
    Period = (micros()-StartPeriod);//calculate period throughout N cycles
    NewSignal = true;//to restart sampling the Start pivot
                   
     x=0;//reset counter
    New_status = true;//let main loop calculate the velocity and send to the monitor after new period has been calculated.>>>>>
  }
  
  x++;
}

Here is the graph plotting angular rate (RPM) vs. time. The noise just goes upward in one direction. =(

If you're using a simple photo-interrupter style encoder it probably outputs analog waveforms,
which need conditioning before going to a digital pin. You'll probably need
some low-pass filtering followed by hysteresis (74HC14 perhaps) to generate
clean logic signal.

You may have to adjust the collector load resistor to put the output voltage in the right range
for a 74HC14, and your RC LPF cutoff frequency should be rather higher than the maximum
pulse rate you expect to see.

Route your shielded encoder wiring well away from the power wiring of course.

Thank you, I'm gonna try the way you suggest. Anyway, I still doubt if the problem is about the interrupt. I tried various way before coming to this conclusion. I tried another source to generate pwm, so my uno just has one task which is to read the encoder interrupt. And there's almost no noise. But with that way, I can't control exactly how much pwm i'm feeding the motor

Before using an array to store the values, I use Serial.print() to send the data instantly to the monitor. And there'smore than a huge amount of noise.

JohnLincoln:
Can you show us a schematic of your encoder?

Here it is, i don't have BL motor part in my Fritzing gallery. So I take the servo template instead.

The diode P1 is drawn the wrong way around. If you have wired it that way, it is hard to see how the circuit would work at all.

jremington:
The diode P1 is drawn the wrong way around. If you have wired it that way, it is hard to see how the circuit would work at all.

I would really appreciate if you could show me what I did wrong! Thanks !

A photodiode is no different from a diode, except that the container is see-through.
Connect it as you have and it will pull the pin high all the time. Forward bias it and
it conducts.

You need to connect it in reverse so that the photo-current contributes to the
reverse-leakage current.