Strange behaviour monitoring PWM input on a Mega

I have another weird one !

I need to control a rotary position actuator. Its an intelligent device which requires a 5% to 95% 250Hz PWM to position it, and it feeds back its actual position with a similar 5% to 95% 250z PWM signal (nominally 0-5v but the oscilloscope actually measures about 0.1v to 4.8v).

My 250Hz output works perfectly and the actuator positions itself accurately all the time, and the feedback signal, as measured with a logic analyzer, is correct and rock solid.

I'm feeding the feedback signal into Mega Pin 3 and monitoring it via the following simple routine ...

void PWM_Input_Rising()
{
  attachInterrupt(digitalPinToInterrupt(FeedbackPWMPin), PWM_Input_Falling, FALLING);
  prev_time = micros();
}

void PWM_Input_Falling()
{
  attachInterrupt(digitalPinToInterrupt(FeedbackPWMPin), PWM_Input_Rising, RISING);
  PWMInDutyCycle =  ((4000 - (micros()-prev_time)) / 4);
}

What I'm finding is that the calculated feedback duty cycle is accurate about 70% of the time but the other 30% of the time I get spurious seemingly random numbers. The closer I get to 95% the worse it gets ... HOWEVER, if I run exactly the same code on an Uno the feedback is rock solid, never misses a single beat.

I've tried 2 Unos and both are perfect. I've tried 3 Megas and all have the same problem. There is no electrical noise on the feedback line nor is there any variation in the pulse width when checking with a logic analyzer so clearly there is some difference between the Uno and the Mega that I'm missing.

An ideas ?

Gareth

Could You please post a link to the datasheet for that actuator? The wiring could also be interesting.

Unfortunately there is no datasheet, it's just what I worked out through trial and error. Its only got 4 pins in the plug.

Pin 1 - 0v
Pin 2 - 12v
Pin 3 - Position Signal (12v PWM 250 Hz)
Pin 4 - Feedback Signal (5v PWM 250 Hz)

Initially I generated the output PWM by manipulating Timer 1 but it occurred to me that that could be affecting the input interrupt timing so I removed that and just bit banged it. I got the same result. Perfect on the Unos, numerous random inputs on the megas.

Not what was asked for in reply #2. The entire code is generally asked for. Snippets are not rewarding.

I understand mate but what was asked for was a datasheet and sadly there isn't one.

I also hear you about code snippets but the entire code does a myriad of other things and is more than 3,000 lines long. If anybody wants to wade through all that they're welcome ! :smiley:

Ironically I only switched to the mega because I was running out of room in the Uno and getting warnings about possible unpredictable operation, yet it works perfectly on the Uno. You couldn't make it up :laughing:

I do think you are right that its something electrical. I just tried removing the actuator and linking the pwm output pin directly to the feedback input pin and the feedback tracks the output perfectly on both uno and mega so it doesn't seem like a code issue.

Possibly the rising/falling trigger voltage is different between uno and mega ?

See this link

1 Like

I maintained a system of some 250 000 lines during 13 years....
The search function in the IDE does nice things.......

The guess is that You used the wrong pins on the Mega, pins not doing the same as the UNO pins.

Mega code and Mega wiring, UNO code and UNO wiring. There the answer is hiding.

Everything works except the PWM feedback consistency.

I've used the same feedback pin on the mega and on the uno (Digital Pin 3) and verified that that pin isn't referenced anywhere else. I'm aware that interrupt mapping is different between Mega and Uno (hence the use of digitalPinToInterrupt) and all timers are default.

I've verified that the actual signal is stable. I'm pretty sure its NOT a coding issue as it works perfectly on both Uno and Mega if I just wire the pwm output (5v side) directly into pin 3.

Anyway I'll knock up a bare bones version that just monitors pin 3 and calculates the duty cycle and see if that gives me the same problem.

Appreciate the suggestions :slight_smile:

So D3 is used. I don't know the Mega pin functionality. Some pins don't provide the same functions, but which ones?
No more suggestion here.

At least say you know how to use volatile variables and assure atomic access to any that are multibyte.

a7

It is always a good idea to get a new device running with the simplest code required to exercise all the functions.

In case of problems you then can comfortably post the entirety of that code on the forum, and have some hope for helpful answers.

But don't kid yourself. 3,000 lines is not a large program.

Ok, so I wrote a bare bones version (below) and it seems to work perfectly on both the Uno and the Mega, whereas the original still only works properly on the Uno. I'm confident that the pin isn't referenced anywhere else so I'm going to assume that something in one of the libraries is messing with the timing on the Mega but not the Uno.

#include <TimerOne.h>

#define PowerRelayPin 5            // Switch on power to device
#define PWMRelayPin 4              // Enable 12v PWM to device
#define PWMInPin 3                 // Interupt pin to capture pwm input rising / falling
#define PWMOutPin 9                // PWM output to 12v level change transisistor. Change to
                                   // pin 11 on Mega as Timer 1 does not control Mega pin 9

const int PWMOutFrequency = 250;   // 250Hz Output
float PWMOutDutyCycle = 80.0;      // run a fixed 80% signal for testing
unsigned long TriggerTime;         // Rising edge trigger time in micros
unsigned long PWMInDutyCycle;      // Calculated duty cycle (500 = 50.0%)

int DisplayCounter = 0;
bool Triggered = false;

unsigned long ChangeOutputTimer;

void setup() 
{
  pinMode(PowerRelayPin, OUTPUT);
  pinMode(PWMRelayPin, OUTPUT);  
  pinMode(PWMOutPin, OUTPUT);
  pinMode(PWMInPin, INPUT);

  Serial.begin(115200);

  digitalWrite(PowerRelayPin, HIGH);
  digitalWrite(PWMRelayPin, HIGH);  
  
  Timer1.initialize(int(1000000 / PWMOutFrequency));  
  
  attachInterrupt(digitalPinToInterrupt(PWMInPin), PWM_Input_Rising, RISING);
}

void loop() 
{
  if ((millis() - ChangeOutputTimer) > 30000)
  {
    ChangeOutputTimer = millis();
    if (PWMOutDutyCycle == 80)
    {
      PWMOutDutyCycle = 50;
    } else PWMOutDutyCycle = 80;
  }
  
  Timer1.pwm(PWMOutPin, ((PWMOutDutyCycle / 100) * 1023));  
  
  if (Triggered == true)
  {
    Triggered = false;
    Serial.print(PWMInDutyCycle);Serial.print('\t');
    DisplayCounter ++;
    if (DisplayCounter == 24)
    {
      DisplayCounter = 0;
      Serial.println();
    }
  }
}

void PWM_Input_Rising() {
  attachInterrupt(digitalPinToInterrupt(PWMInPin), PWM_Input_Falling, FALLING);
  TriggerTime = micros();
}

void PWM_Input_Falling() {
  attachInterrupt(digitalPinToInterrupt(PWMInPin), PWM_Input_Rising, RISING);
  PWMInDutyCycle = ((4000 - (micros()-TriggerTime)) / 4);
  Triggered = true;
}

I guess now I have to re-introduce the rest of the code section by section until I find the culprit.

Again, thanks for the comments and suggestions.

G

You've already got bugs. Those variables need to be marked volatile.

Since PWMInDutyCycle is multibyte and changed in an interrupt, you need a critical section for this. You can't leave the possibility that an interrupt happens between the time the first byte is read and the last byte.

That may be a part of your larger problem: