0
votes

I am trying to read an input signal from a source that is a PWM signal. In my research I have found some useful articles out found here: http://www.instructables.com/id/Arduino-Frequency-Detection/?ALLSTEPS and here: http://www.camelsoftware.com/2015/12/25/reading-pwm-signals-from-an-rc-receiver-with-arduino/. The first article is a little beyond my experience level, and would not be helpful if I used anything other then a uno, although it seems to perform extremely well. The second shows a method I was better able to understand.

I have been using the following code somewhat successfully:

#define input_pin 2

volatile unsigned long timer_start;
volatile int pulse_time;

volatile int last_interrupt_time;  
void calcSignal() 
{
    last_interrupt_time = micros(); 

    if(digitalRead(input_pin) == HIGH) 
    { 
       timer_start = micros();
    }  
    else {   
       if(timer_start != 0)
       { 
           //record the pulse time
           pulse_time = ((volatile int)micros() - timer_start);
           //restart the timer
           timer_start = 0;
       }
    } 
} 


void setup() 
{
    timer_start = 0; 
    attachInterrupt(0, calcSignal, CHANGE);
    Serial.begin(115200);
} 

void loop()
{
    Serial.println(pulse_time);
    delay(20);
} 

The problem with this setup for my application is the interrupt is only triggered by a change in state, when realistically I need to know the duration of how long it is high and how long it is low. A picture of the ideal signals can be seen here with various duty cycles (https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM). I tried changing the interrupt mode from CHANGE to LOW and HIGH, but did not get any creditable results, as it only output zeros on the serial monitor. Is there something I am missing or an alternative library/method that can be used? I am somewhat new to programming, so I have some understanding, but am by no means a programmer.

2
Just wondering - how does this method of measuring PWM handle 0% and 100% PWM ratios? Thanks,Michael Vincent
@MichaelVincent I have not tried a 100% pwm signal, but I have tried a 0% signal (unplugged input) and it prints a stream of zeros on the serial monitor.paperstsoap
Thanks. I think there might be a logical issue with your code. It may never occur, or only occur rarely. Imagine you have PWM input at, say, 77% duty cycle. The input goes low, you grab the new start time, then the PWM duty cycle goes to 0%. Will pulse_time be updated to show 0, or will it continue to show 77% ? I think the issue, if it exists, is in mactro's code, too. Kind regards,Michael Vincent
@MichaelVincent I have suspected a similar idea, however I was not familiar with an interrupt method and found this code on a site explaining various methods to read a pwm signal. That being said, it very well could have errors. I am going to mess with it and see what type of results I get.paperstsoap
I wish I had a solution - I'm sure there is one - but I don't. I'm looking at reading a PWM input from a sensor, but the duty cycle range won't extend down to 0 or up to 100. So the code here will work for me - and depending on your application may well work for you. All the best with your project,Michael Vincent

2 Answers

0
votes

What about something like this:

unsigned long     timeout = 100000L; // reasonable timeout around 100ms?
unsigned long     high_us = pulseIn(pinNo, HIGH, timeout);
unsigned long      low_us = pulseIn(pinNo, LOW,  timeout);
unsigned long thousandths = (high_us*1000) / (high_us+low_us);

However it won't be able to measure one period HI/LO. It'll be measured over two of them: >HI< lo hi >LO<.

And if it's not what you want, you can dig into AVR DataSheet and try to get Timer Input Capture interrupts working.

0
votes

I think you can go with your method, just by adding another timer:

void calcSignal()  {
    if(digitalRead(input_pin) == HIGH) 
    { // transition from LOW to HIGH
       timerH_start = micros();
       //record the pulse time 
       pulse_time_L = ((volatile int)micros() - timerL_start);
    }  
    else { // transition from HIGH to LOW
       timerL_start = micros();   
       //record the pulse time 
       pulse_time_H = ((volatile int)micros() - timerH_start);      
    }  
}