Measure speed with ams5311 PWM output

Hi, I want to use this magnetic encoder sensor: ams5311 with an off-axis magnetic ring to measure speed on a wheel. I know it has Incremental and Serial output but I thought I could use PWM output and keep the hardware easy (just 3 wire output including GND and 5V and just one interruption on arduino used) and decode the information from there. The PWM has a cycle time of 4098us that fills to 100% every 5.6250 grades.

To start, I am capable to read the PWM duty cycle with the code below. When the wheel is static I get the time measure of 4095 us that results from summing ton + toff but when I spin the wheel at constant velocity this change at non sense frequency, some times it sums 7700 and others 3400 and some times 4095 but is never constant.

I have already confirmed with the Oscilloscope that the frequency is always constant. Can you help me figuring out if I need to read the PWM in another way or if trying to get velocity from the PWM is madness?.

Thanks.

#include <Streaming.h>
  
volatile unsigned long StartTime = 0;
volatile unsigned long CurrentTime =  0;
volatile int ton = 0;
volatile int toff = 0;
volatile int ton_f = 0;
volatile int toff_f = 0;

int pwm = 0;
 
void setup() {
  Serial.begin(115200);
  attachInterrupt(1, rising, RISING);
  StartTime = micros();
}

void loop() {

    CurrentTime = micros();   
    pwm = round(((float)ton/((float)ton+(float)toff))*100.0);
    Serial <<ton+toff<<","<< CurrentTime-StartTime << "," << pwm<< endl;
    StartTime = CurrentTime;
 
}

void rising() {
  attachInterrupt(1, falling, FALLING);
  ton_f = micros();
  toff = micros() - toff_f;
}
 
void falling() {
  attachInterrupt(1, rising, RISING);
  ton = micros()-ton_f;
  toff_f = micros();
}

Nice job using code tags on your first post.

The data sheet tells me that you are headed in the wrong direction.

Pay particular attention to Figure 3 in section 7 on page 10.

Depending on your rotational speeds, the magnet wheel you have, and the resolution you need, I would either read the index pulse for every pole pair or use the standard quadrature output.

The pwm output varies through each pole pair and is more appropriate to determine fine position.

Thanks for your quick answer, yeah it seems that I am complicating my life, I was trying to produce this index pulse detecting the change from 100% to 0% or 0% to 100% using a Gaussian probability distribution on the extremes and the difference between last measured values of PWM (code below) but is hard to tune it for different speeds because some times the sensor does not read the same values, i.e. some times it changes from 80% to 15% or from 95% to 5% etc. If you think I could give this a chance due to my lack of programming skills on the code below it would help me a lot but if you recommend me just to change pins I will be grateful anyway.

Cheers.

#include <Streaming.h>

//Variables for interruptions
unsigned long StartTime = 0;
unsigned long CurrentTime =  0;
unsigned long ElapsedTime = 0;
volatile unsigned long ton = 0;
volatile unsigned long toff = 0;
volatile unsigned long ton_f = 0;
volatile unsigned long toff_f = 0;

float actual_pwm=0.0, last_pwm=0.0, angle=0.0, offset_angle=0.0;

//Variables to detect change
float derivative, probability_right, probability_left;

//Variables to tune change detector
float sigma = 0.3;
float derivative_detector = 0.4;
float probability_detector = 0.4;
 
void setup() {
  Serial.begin(115200);
  StartTime = micros();
  attachInterrupt(1, rising, RISING);
  actual_pwm = ((float)ton/((float)ton+(float)toff));
  offset_angle = actual_pwm * 5.6250;
  Serial << "Offset angle=" << offset_angle << endl;
}

void loop() {

      actual_pwm = ((float)ton/((float)ton+(float)toff));
      
      //CurrentTime = micros();
      //ElapsedTime = CurrentTime - StartTime;
      //derivative = round(((float)pwm_noise - (float)pwm_value)/(float)(ElapsedTime*0.001));
      
      derivative = (float)actual_pwm - (float)last_pwm;
      
      probability_right = exp(-(pow((float)actual_pwm-1.0,2)/(2.0*pow(sigma,2))));
      probability_left = exp(-(pow((float)actual_pwm,2)/(2.0*pow(sigma,2))));
      
      if (derivative < -derivative_detector && probability_left > probability_detector)
      {
        angle+=5.6250;
        Serial << "Derivative=" << derivative << "," << "pwm(x)=" << last_pwm << " " << "pwm(x+t)=" << actual_pwm << " "  << "P_l=" << probability_left << " " << "P_r=" << probability_right <<  " " << "Angle=" << angle << endl;
      }
      if (derivative > derivative_detector && probability_right > probability_detector)
      {
        angle-=5.6250;
        Serial << "Derivative=" << derivative << "," << "pwm(x)=" << last_pwm << " " << "pwm(x+t)=" << actual_pwm << " "  << "P_l=" << probability_left << " " << "P_r=" << probability_right <<  " " << "Angle=" << angle << endl;
      }
      
      if (angle > 360)
      angle = 1;
      if (angle < 0)
      angle = 359;
      
      //StartTime = micros();
      last_pwm = actual_pwm;

}
 
void rising() {
  attachInterrupt(1, falling, FALLING);
  ton_f = micros();
  toff = micros() - toff_f;
}
 
void falling() {
  attachInterrupt(1, rising, RISING);
  toff_f = micros();
  ton = micros() - ton_f;
}

I'm not clear about your physical system, and what you are trying to measure. It looks like its some sort of angular variation. What range of motion do you have? Do you have multiple rotations? How fast is the system moving?

If you can explain the application, I might be better able to advise on one of the reading modes. Certainly where you are headed looks overly complex for reading an encoder.

Your approach of reattaching the interrupt and switching interrupt modes from RISING to FALLING is not good practice. It is better to have the mode as CHANGE, and read the pin to determine the state.

Hi, my application is exactly like this one: mag-encoder-off-axis, I want to measure the speed of the wheel. The speeds I manage are up to 150 rpm.

Thanks for the tip on interruptions, I am just trying to identify the change between poles on this magnetic ring: Magnetic ring by the changes on the PWM signal.

I am going to give it a try with other outputs, I would want to keep the hardware simple by using just 3 wires, I will post if I get a solution.

Thanks.

I would want to keep the hardware simple by using just 3 wires,

I would use the index pulse. Your ring has 128 poles, and you will get one index pulse per pole pair, or 64 pulses per revolution of the ring.

You can determine the speed by determining the interval between each pulse, or the interval for some number of pulse counts which provides a bit of averaging.

Its also very simple to count the number of pulses in a fixed time period. You should get 160 pulses per second at 150 rpm, but the +/- of one pulse at the time boundary will give a 1.5% error