This is probably something simple, but I can't figure it out.
I have a 100Hz PWM signal. I'm trying to measure the duty cycle to determine the state of the device sending the signal. To test this idea, i used this code;
const int PWMGenerator = 5; //980Hz pulse generator arduino itself
const int PulseIN = 9; // pulse counter pin
unsigned long ONCycle; //oncycle variable
unsigned long OFFCycle; // offcycle variable got microsecond
unsigned long T; // total time to one cycle ONCycle + OFFcycle
unsigned long F; // Frequency = 1/T
float DutyCycle; // D = (TON/(TON+TOFF))*100 %
void setup()
{
pinMode(PWMGenerator, OUTPUT);
pinMode(PulseIN, INPUT);
Serial.begin(115200);
analogWrite(PWMGenerator,100); //sample pulse 980Hz
}
void loop()
{
ONCycle = pulseIn(PulseIN, HIGH);
OFFCycle = pulseIn(PulseIN, LOW);
T = ONCycle + OFFCycle;
DutyCycle = (ONCycle / T) * 100;
F = 1000000 / T; // 1000000= microsecond 10^-6 goes to upper
Serial.print("High Time = ");
Serial.print(ONCycle);
Serial.print(" us");
Serial.print("\n");
Serial.print("Low Time = ");
Serial.print(OFFCycle);
Serial.print(" us");
Serial.print("\n");
Serial.print("Pulse Width = ");
Serial.print(T);
Serial.print(" us");
Serial.print("\n");
Serial.print("Frequency = ");
Serial.print(F);
Serial.print(" Hz");
Serial.print("\n");
Serial.print("DutyCycle = ");
Serial.print(DutyCycle);
Serial.print(" %");
Serial.print("\n");
delay(1000);
}
The serial monitor spits out everything correctly, except for the duty cycle;
19:14:22.792 -> High Time = 396 us
19:14:22.792 -> Low Time = 620 us
19:14:22.792 -> Pulse Width = 1016 us
19:14:22.826 -> Frequency = 984 Hz
19:14:22.826 -> DutyCycle = 0.00 %
What am I missing? Using an Arduino Nano Every. I tried varies data types, but it always give me 0. Any ideas?
Should also work to force a floating point calculation. Simply defining DutyCycle as float is not enough to prevent it being treated as an integer calculation.
Now I am trying to do it with the 100Hz signal, and I can't even read the on-time; Always return 0. Copied the code 1:1, so all the declarations should be good;
const int PWM_PIN = 9;
unsigned long DutyCycle;
void setup()
{
Serial.begin(115200);
Serial.println("********** - System Initialized - **********");
pinMode(PWM_PIN,INPUT);
}
void loop()
{
elapsedMicros = micros() - previousMicros;
if ((elapsedMicros >= lowTime) && (highLow == 0))
{
previousMicros = previousMicros + lowTime;
digitalWrite(pin8, HIGH);
highLow = 1; // started high period
}
else if ((elapsedMicros >= highTime) && (highLow == 1))
{
previousMicros = previousMicros + highTime;
digitalWrite(pin8, LOW);
highLow = 0; // started low period
}
// "most of the time" (processor-wise) the above do nothing as the proper time hasn't elapsed
// use that downtime to adjust the duty cycle
if (digitalRead(button)==HIGH)
{
if (StatusFlag == 2) // and the status flag is 2 (Current status is pass through)
{
StatusFlag = 0; // Make status flag 0 (20% DC)
lowTime = 8000;
}
else if (StatusFlag == 0) // otherwise...
{
StatusFlag = 1; // make status flag 1 (80% DC)
lowTime = 2000;
}
else if (StatusFlag == 1) // otherwise...
{
StatusFlag = 2; // make status flag 2 (Pass Through)
RGB_color(0, 0, 0); // LED is off
lowTime = 1000;
}
delay(300); // wait a sec for the hardware to stabilize
highTime = period - lowTime;
OnTime = pulseIn(PWM_PIN, HIGH);
Serial.print("Measured High Time: ");
Serial.print(OnTime);
//Serial.println("%");
}
}
Can't quite figure out why it reads 0. The signal going to pin 9 is a 100Hz, 0-5.5V PWM signal. Thanks for your suggestions.
So, I stripped the code to the absolute bare bones;
byte pin8 = 8;
const int PWM_PIN = 9;
unsigned long currentMicros;
unsigned long previousMicros; // timer for PWM generator switch hi/lo
unsigned long elapsedMicros; // timer for PWM generator switch hi/lo
unsigned long previousMicrosSerial; // timer for serial print command
unsigned long elapsedMicrosSerial; // timer for serial print command
unsigned long period = 10000UL; // microseconds, 0.01S 1/.01 = 100 Hz
unsigned long highTime= 2000UL; // microseconds
unsigned long lowTime; // microseconds
unsigned long interval = 5000000UL; // interval set to 5 seconds
byte highLow = 1; // flag to show which state output is in
unsigned long OnTime;
void setup()
{
Serial.begin(115200);
Serial.println("********** - System Initialized - **********");
pinMode(PWM_PIN,INPUT);
pinMode (pin8, OUTPUT);
digitalWrite (pin8, LOW);
lowTime = period - highTime; // with numbers above, 10000 - 2000 = 8000
currentMicros = micros();
previousMicros = currentMicros;
elapsedMicrosSerial = micros();
previousMicrosSerial = currentMicros;
}
void loop()
{
elapsedMicros = micros() - previousMicros;
elapsedMicrosSerial = micros()-previousMicrosSerial;
if ((elapsedMicros >= lowTime) && (highLow == 0))
{
previousMicros = previousMicros + lowTime;
digitalWrite(pin8, HIGH);
highLow = 1; // started high period
}
else if ((elapsedMicros >= highTime) && (highLow == 1))
{
previousMicros = previousMicros + highTime;
digitalWrite(pin8, LOW);
highLow = 0; // started low period
}
if (elapsedMicrosSerial >= interval)
{
OnTime = pulseIn(PWM_PIN, HIGH);
Serial.print("Measured On Time: ");
Serial.print(OnTime);
Serial.println(" us");
previousMicrosSerial = previousMicrosSerial + interval;
}
}
This generates the 100Hz PWM signal on pin 8, that I am then trying to read on pin 9. Hooking up the scope, I can see what goes wrong. The pulseIn command times out, because the processor does not keep generating the signal because it is busy executing the pulseIn command.
Ultimately, the 100Hz signal I am trying to read comes from my engine ECU, so it will be an external signal. I don't have anything to generate an external signal at the moment.
Is there another way to read this signal generated on pin 8 without interrupting the code?
You could use a pin change interrupt, configured for pin 9. The interrupt service routine should be quick to execute and cannot, for example, use Serial.print() , delay() etc. Usual is to just set a flag in the Interrupt service routine and, in the loop, act on that flag, then unset it.
I noticed that the duty cycle calculation is not 100% precise. It always returns a round number for some reason. However, for what I am doing, it does not matter. I need to know if the DC is either 20% or 80%, so a ball park number is good enough.
Do you have an idea why it is spitting out rounded numbers?