*SOLVED* Arduino Uno *random* PWM pause

Hello all, long time forum ghost here who has never had the need to post, except now due to needing help! Hoping someone can point me in the right direction here…

I’m not sure if I’m posting in the right section as my problem could be either related to electronics or code, so please excuse me if this is in the wrong place.

I’m working on a project involving basically a “Arduino Uno on a PCB” designed to drive a solenoid valve with PWM. The Uno is setup as a SPI slave (Thanks to Nick Gammon’s excellent page about it) to receive a byte of information from another uC, this byte contains the PWM value to analogWrite to the pin for the solenoid. Schematic below.

F1 is a 2A fuse
Q1 is a FQP30N06L MOSFET
D3 is actually a 1N4002 diode
Q5 is a FOXSLF/160-20 crystal with 20pF load capacitance
DGND and GND and joined at a common point on the board.

Not shown on this schematic is also two LEDs driven by pin D6 and pin D7, with a 1kOhm resistor to ground. These correspond to SOLENOID_MONITOR_LED_PIN and HEARTBEAT_LED_PIN as per the code.

It works fairly well currently, however I’m hunting down an issue where an occasion seemingly random stutter or pause occurs with the solenoid valve. I’ve scoped it and the pause is definitely coming directly from the pin on the Uno. It appears that 1 or 2 cycles are missing from the wave form, so the level is just at the low state during this time.
At first I thought the issue was related to some type of noise from the solenoid or MOSFET getting back into the Uno, however I’ve fiddled with the code and can basically make the issue go away by taking out a bit of the code.

Sketch definitions, setup, and SPI ISR:

byte command = 0;
int count = 0;
#define SPI_MSGTYPE_RECEIVE_SOLENOID_DUTY 0x73

byte solenoidPwmDuty = 0;
unsigned long timeLastReceived = 0;
#define FAILSAFE_TIMEOUT 240
#define SOLENOID_PIN 9
unsigned long timeLastAnalogWrite = 0;
#define ANALOG_WRITE_TIME 40
#define HEARTBEAT_LED_PIN 7
#define HEARTBEAT_INTERVAL 1000
unsigned long timeLastHeartbeat = 0;
#define SOLENOID_MONITOR_LED_PIN 6
unsigned long timeLastSolMonLedToggled = 0;
boolean heartbeatLedState = false;
boolean solenoidLedState = false;

byte in = 0;
boolean avail = false;

void ss_falling(){
	command = 0;
}

void setup(){
	TCCR1B = TCCR1B & 0b11111000 | 0x05;
	
	pinMode(MISO, OUTPUT);

	SPCR |= _BV(SPE);
	SPCR |= _BV(SPIE);
  
	attachInterrupt (0, ss_falling, FALLING);
  
	pinMode(SOLENOID_PIN, OUTPUT);
	analogWrite(SOLENOID_PIN, 0);

	pinMode(HEARTBEAT_LED_PIN, OUTPUT);
	digitalWrite(HEARTBEAT_LED_PIN, LOW);
	
	pinMode(SOLENOID_MONITOR_LED_PIN, OUTPUT);
	digitalWrite(SOLENOID_MONITOR_LED_PIN, LOW);

	//Serial.begin(115200);
}

Loop code for when issue is present:

void loop() {
  
  if(millis() - timeLastHeartbeat > HEARTBEAT_INTERVAL){
		heartbeatLedState = !heartbeatLedState;
		digitalWrite(HEARTBEAT_LED_PIN, heartbeatLedState);
        timeLastHeartbeat += HEARTBEAT_INTERVAL;
	}
	if(millis() < timeLastHeartbeat){
		timeLastHeartbeat = millis();
	}

	if(solenoidPwmDuty < 0 || solenoidPwmDuty > 255){
		solenoidPwmDuty = 0;
	}
	
	if(millis() - timeLastAnalogWrite > ANALOG_WRITE_TIME){
		analogWrite(SOLENOID_PIN, solenoidPwmDuty);
		timeLastAnalogWrite += ANALOG_WRITE_TIME;
	}
	if(millis() < timeLastAnalogWrite){
		timeLastAnalogWrite = millis();
	}
	
	if(millis() - timeLastReceived > FAILSAFE_TIMEOUT){
		solenoidPwmDuty = 0;
		digitalWrite(SOLENOID_MONITOR_LED_PIN, LOW);
    solenoidLedState = false;
	}
	else{
		if(solenoidLedState == false){
      digitalWrite(SOLENOID_MONITOR_LED_PIN, HIGH);
      solenoidLedState = true;
		}
	}
	if(millis() < timeLastReceived){
		timeLastReceived = millis();
	}

}

Loop code for when issue is NOT present:

void loop() {
  
//  if(millis() - timeLastHeartbeat > HEARTBEAT_INTERVAL){
//		heartbeatLedState = !heartbeatLedState;
//		digitalWrite(HEARTBEAT_LED_PIN, heartbeatLedState);
//        timeLastHeartbeat += HEARTBEAT_INTERVAL;
//	}
//	if(millis() < timeLastHeartbeat){
//		timeLastHeartbeat = millis();
//	}

	if(solenoidPwmDuty < 0 || solenoidPwmDuty > 255){
		solenoidPwmDuty = 0;
	}
	
	if(millis() - timeLastAnalogWrite > ANALOG_WRITE_TIME){
		analogWrite(SOLENOID_PIN, solenoidPwmDuty);
		timeLastAnalogWrite += ANALOG_WRITE_TIME;
	}
	if(millis() < timeLastAnalogWrite){
		timeLastAnalogWrite = millis();
	}
	
//	if(millis() - timeLastReceived > FAILSAFE_TIMEOUT){
//		solenoidPwmDuty = 0;
//		digitalWrite(SOLENOID_MONITOR_LED_PIN, LOW);
//    solenoidLedState = false;
//	}
//	else{
//		if(solenoidLedState == false){
//      digitalWrite(SOLENOID_MONITOR_LED_PIN, HIGH);
//      solenoidLedState = true;
//		}
//	}
//	if(millis() < timeLastReceived){
//		timeLastReceived = millis();
//	}

}

So by taking out the code that toggles the LEDs, I can make the stutter go away. This is the point where I go… WTF!! I’m not quite sure why/how the above would be affecting the PWM signal, which AFAIK is controlled by the timer portion of the Uno which shouldn’t be affected by loop code…?! Maybe not.

Any pointers, guidance, or help would be much appreciated :slight_smile:

Cheers,
Matt

I don't see in the diagram how you power the Arduino. And if you have used decoupling on the VCC line. Leo..

Hi,
Does this stutter appear at any particular duty cycle, for example always when duty cycle is less than 10%.

I’m thinking that the time it takes to do the LED heartbeat loop might be a significant time compared to the time of the low duty cycle, so it causes a jump in OFF/ON states.

Just a thought.

Tom… :slight_smile:
PS Does this happen if the solenoid is not connected.

Wawa: I don't see in the diagram how you power the Arduino. And if you have used decoupling on the VCC line. Leo..

Here is the schematic for the power supply part of the circuit. |500x154

TomGeorge: Hi, Does this stutter appear at any particular duty cycle, for example always when duty cycle is less than 10%.

I'm thinking that the time it takes to do the LED heartbeat loop might be a significant time compared to the time of the low duty cycle, so it causes a jump in OFF/ON states.

Just a thought.

Tom..... :) PS Does this happen if the solenoid is not connected.

The stutter appears to happen on a range of duty cycles... I've seen it at low as 40/255 and as high as 180/255, however most of the time I test with about 120/255 and it occurs here as well, so I'd say it happens for the full range. It's a bit harder to tell above/below those numbers as the solenoid working range seems to be about 20 - 200 / 255.

I have not been able to test it with the solenoid disconnected, as I am only using a primitive scope. I hold the current waveform after I hear the solenoid stumble, and then check it out.

Correct me if I am wrong - I thought that once you do an analogWrite and set the duty cycle, the PWM pin will just keep outputing that duty cycle regardless of what's happening in the main loop until the next analogWrite of the PWM pin?

Some extra info... I've written a dummy sketch for the SPI master that floods the Uno with SPI data with no delays and that doesn't seem to reproduce the issue, so I think I can count SPI out as being the cause.

Hi, In your sketch you commented out two areas of the code, what happens if you only comment out one area.

Tom... :)

TomGeorge: Hi, In your sketch you commented out two areas of the code, what happens if you only comment out one area.

Tom... :)

Hi Tom,

Thanks for giving me the motivation to troubleshoot this further! I think I tried isolating it further yesterday however I don't think I noticed much difference, so maybe the issue just didn't occur at that time!

So it was the 2nd segment of commented out code which appeared to be the problem. I started thinking that every 2nd SPI transfer or something had dodgy data in it, so I went back to the master and put some extra code in to receive back the same value I sent it on the previous transfer. Funnily enough - ever single time it came back spot on, so that gave me the confidence to say that SPI was all good! So I then went back to the Uno/328P and did some more testing there... I put some code in to blink the LEDs when entering and exiting that specific switch case and started noticing some really weird behaviour, the LEDs were turning on and off and different times, when theoretically they should be turning on/off at the same time. So what else was in that section of code? A freakin millis() !!!!! Ahhhhh never put a millis() inside a interrupt... that's all I'm saying!!! Now that this has come up, I do remember running into this in the past using normal pin interrupts, however I thought the SPI interrupt was different somehow.

Long story short... this was a code problem so I put in a received = true setup in the interrupt and then in the main loop looked for true and then set the lastReceivedTime as millis().

Also just saw that I didn't add the code earlier for the SPI ISR, that might have helped!!

Cheers!