Go Down

Topic: Sensor that gives PWM reading in pulse train (Read 705 times) previous topic - next topic

altagest

Hi every1!

I'm having a problem, already posted in Italian section, but I'm posting here so maybe the solution can be helpful to people around there.
I have a sensor which makes 3 readings in a pulse train (inverted PWM signal). Here's an image that maybe explains better:



As you can see, it is inverted PWM output. That's why I need to take the time of the LOW signal states.
Then, I need to calculate the three different values with this simple formulas:

PWM1 = T1/T
PWM2 = T2/T
PWM3 = T3/T

the period of T should be 1.2 sec = 1200 milliseconds.

I would like to do this with pulseIn() or interrupt, but maybe pulseIn() is much more easier. I don't have any idea on how to do this. Any ideas? Thanks!

dc42

#1
Oct 25, 2013, 08:49 pm Last Edit: Oct 25, 2013, 08:52 pm by dc42 Reason: 1
I would use an interrupt, with mode CHANGE so that you get an interrupt on both edges. In the interrupt service routine, get the current time by calling micros(). If the pin is low, then you are at the start of T1, T2 or T3, so just store the value you read (in an 'unsigned long' variable). If the pin is high, subtract the value you stored earlier, that gives you the T1, t2 or T3 value in microseconds. Use micros() rather than millis(), because millies() is less accurate and is subject to jitter - it sometimes advances by 2 instead of 1.

Something like this (warning: untested code!):

Code: [Select]

unsigned long lastFallingTime;
volatile unsigned long lettura[3];
byte index = 0;

void setup()
{
 attachInterrupt(2, isr, CHANGE);
 Serial.begin(19200);
}

void isr()
{
 unsigned long now = micros();
 if (digitalRead(2) == LOW)
 {
   // falling edge seen
   if (now - lastFallingTime > 2000000UL)
   {
      index = 0;   // we've seen the pause, so reset the counter
   }
   lastFallingTime = now;
 }
 else
 {
    // rising edge seen
   if (index < 3)
   {
     lettura[index] = now - lastFallingTime;
     ++index;
   }
 }
}

void loop()
{
 delay(5000);
 for (int i = 0; i < 3; ++i)
 {
   noInterrupts();
   unsigned long t = lettura[i];
   interrupts();
   Serial.print("Lettura ");
   Serial.print(i);
   Serial.print('=');
   Serial.println(t);
 }
}


[EDIT: corrected code so that it at least compiles without warnings]
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

Thanx dc42, that's a nice idea.
If I'm not doing something wrong... and if I understood correctly, 100% duty cycle (in this case LOW signal) should be 1.200 milliseconds.
If I have to do the following calculation I need:

Code: [Select]

PWM1 = T1/T
PWM2 = T2/T
PWM3 = T3/T


should be
Code: [Select]

PWM1 = lettura[0] / T (T, taking a look at the wave graph above, should be 1.200 milliseconds, is that correct?)
PWM2 = lettura[1]
PWM3 = lettura[2]


What do you think?

dc42

#3
Oct 26, 2013, 11:00 am Last Edit: Oct 26, 2013, 11:02 am by dc42 Reason: 1
From the diagram in your original post, it looks like 20% of T is the minimum, and 80% is the maximum. It's been done this way so that you always see a pulse, otherwise it would be difficult to work out what is going on when some pulses were at 0% or 100%. So your calculation will give values in the range 20% to 80%.

Do you know how accurate the 1.2 seconds is supposed to be? If it is not very accurate, or if you are looking for accuracy better than 0.5% (the accuracy of the Arduino's ceramic resonator), then it would be best to measure the time between falling edges as well, and use that as the reference. Like this:

Code: [Select]

unsigned long lastFallingTime, lastRisingTime;
volatile unsigned int pwm[3];
byte index = 3;    // to avoid storing readings until we are synchronized

void setup()
{
 attachInterrupt(2, isr, CHANGE);
 Serial.begin(19200);
}

void isr()
{
 unsigned long now = micros();
 if (digitalRead(2) == LOW)
 {
   // falling edge seen
   unsigned long interval = now - lastFallingTime;
   if (interval > 2000000UL)
   {
      index = 0;   // we've seen the pause, so reset the counter
   }
   else if (index < 3 && interval >= 0x1000000UL && interval <= 0x1400000UL)   // if between 1000 and 1400ms
   {
     pwm[index] = (unsigned int)(((lastRisingTime - lastFallingTime) * 100)/interval);   // store the PWM value as a percentage
     ++index;
   }
   lastFallingTime = now;
 }
 else
 {
   // rising edge seen
   lastRisingTime = now;
 }
}

void loop()
{
 delay(5000);
 for (int i = 0; i < 3; ++i)
 {
   noInterrupts();
   unsigned int t = pwm[i];
   interrupts();
   Serial.print("PWM ");
   Serial.print(i);
   Serial.print('=');
   Serial.println(t);
 }
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

dc42, thanks for the reply.

Using that code i get these results from the serial monitor:

Code: [Select]


PWM 0=0
PWM 1=0
PWM 2=0



what's wrong?

dc42

Looks like no input is being received. The code I suggested assumes that you are running on an Arduino Uno and you feed the input to digital pin 2 (because that is the pin that triggers interrupt 0). Is that what you have done? Do you have a common ground between the sensor and the Arduino?
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest


Looks like no input is being received. The code I suggested assumes that you are running on an Arduino Uno and you feed the input to digital pin 2 (because that is the pin that triggers interrupt 0). Is that what you have done? Do you have a common ground between the sensor and the Arduino?


Sure yes! Output from the sensor goes to Arduino digital pin 2 and there is ground in common. Maybe there's something with timing in the code? I don't think...

dc42

#7
Oct 28, 2013, 09:16 am Last Edit: Oct 28, 2013, 09:17 am by dc42 Reason: 1
Are you sure the sensor is generating the pulses? If you do a digitalRead of pin 2 in a loop, do you read both highs and lows?

Also, are you certain that T is 1200ms? It doesn't say that in the diagram that you posted.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

If you take a look on the first line just below "PAUSA" in the diagram, you will see that it says

1 sec + 200 msec

oh wait - 1 sec + 200 msec maybe it's not 1.2 secs?

dc42

It's not clear what the 1 sec + 200 msec is, because it doesn't indicate where that time starts from. I think it is the length of the pause. This might not be the same as the time T.

Try this code:

Code: [Select]

void loop()
{
  while (digitalRead(2) == HIGH) {}
  unsigned long fall =micros();
  while (digitalRead(2) == LOW) {}
  unsigned long rise = micros();
  Serial.print("Low="); Serial.print(rise - fall);
  while (digitalRead(2) == HIGH) {}
  fall =micros();
  Serial.print(" High="); Serial.println(fall - rise);
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

That's what I get:

Code: [Select]

Low=44724 High=71020
Low=43992 High=71156
Low=22616 High=1249748
Low=44004 High=71020
Low=43992 High=71156
Low=22628 High=1249764
Low=44000 High=71020
Low=44000 High=71164
Low=22624 High=1249824
Low=44004 High=71012
Low=43992 High=71156
Low=22620 High=1249804
Low=44004 High=71024
Low=43992 High=71156
Low=22776 High=1249636
Low=44004 High=71020
Low=44136 High=71016
Low=22624 High=1249764
Low=44004 High=71024
Low=43996 High=71156
Low=22624 High=1249780
Low=43996 High=71016
Low=43992 High=71156
Low=22640 High=1249764
Low=44000 High=71020
Low=44124 High=71020
Low=22636 High=1249740
Low=43996 High=71016
Low=43984 High=71160
Low=22640 High=1249744
Low=44004 High=71012
Low=43992 High=71156
Low=22624 High=1249784
Low=44004 High=71016
Low=43992 High=71160
Low=22784 High=1249652
Low=43996 High=71020
Low=43992 High=71156
Low=22796

dc42

OK, so T looks like 1.15sec for the first 2 pulses, and 1.27sec for the one that includes the pause. Try the code I suggested in my first reply, but change the constant 2000000UL to 1200000UL.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

Done, but i get this:

Code: [Select]


Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0
Lettura 0=0
Lettura 1=0
Lettura 2=0


dc42

Sorry, I got the attachInterrupt call wrong. It should be attachInterrupt(0, isr, CHANGE).
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

altagest

Nice, this is what I get:

Code: [Select]


Lettura 0=45196
Lettura 1=44724
Lettura 2=23212
Lettura 0=45196
Lettura 1=44584
Lettura 2=23232
Lettura 0=45196
Lettura 1=44748
Lettura 2=23208
Lettura 0=45200
Lettura 1=44584
Lettura 2=23212
Lettura 0=45200
Lettura 1=44724
Lettura 2=23232


Good! We get results. Now I need to calculate PWM1, PWM2 and PWM3. That is simply done by this:

PWM1 = T1 / T
PWM2 = T2 /T
PWM3 = T3/T

That way we designed PWM signal definition. Then, T1 is the total time subtracted the time that signal stays LOW. Am I correct? I think this is already done in the code. Right?

Go Up