0

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.

mactro
  • 518
  • 7
  • 13
paperstsoap
  • 335
  • 4
  • 13
  • Just wondering - how does this method of measuring PWM handle 0% and 100% PWM ratios? Thanks, – Michael Vincent Sep 09 '16 at 07:57
  • @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 Sep 09 '16 at 12:17
  • 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 Sep 09 '16 at 13:02
  • @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 Sep 09 '16 at 14:03
  • 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 Sep 09 '16 at 15:09
  • I should never see a 0 or 100 percent duty cycle is the project, but I do understand your point. The logic should never print a value in those cases because there is never a change. I think that a stream of zeros in this case would suggest that is happening due to the timer being set to zero in the setup loop, defaulting to that value. Out of curiosity what sensor are you using? – paperstsoap Sep 09 '16 at 15:16
  • I'm using some Smartec temperature sensors, probably. Someone has done a library for these which I may use. Its early days. – Michael Vincent Sep 09 '16 at 15:23

2 Answers2

0

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.

KIIV
  • 3,534
  • 2
  • 18
  • 23
0

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);      
    }  
}
mactro
  • 518
  • 7
  • 13
  • This method seems like it is on the right track of what I want to do. I am going to mess around with it today and see what I get. Thank you. – paperstsoap Sep 09 '16 at 14:06