Go Down

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

#### altagest

##### Oct 25, 2013, 07:12 pm
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 pmLast 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

#2
##### Oct 26, 2013, 10:03 am
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/TPWM2 = T2/TPWM3 = 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 amLast 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 synchronizedvoid 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

#4
##### Oct 26, 2013, 02:02 pm

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

Code: [Select]
`PWM 0=0PWM 1=0PWM 2=0`

what's wrong?

#### dc42

#5
##### Oct 26, 2013, 03:06 pm
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

#6
##### Oct 28, 2013, 09:09 am

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 amLast 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

#8
##### Oct 28, 2013, 09:28 am
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

#9
##### Oct 28, 2013, 09:54 am
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

#10
##### Oct 28, 2013, 10:22 am
That's what I get:

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

#### dc42

#11
##### Oct 28, 2013, 10:37 am
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

#12
##### Oct 28, 2013, 10:42 am
Done, but i get this:

Code: [Select]
`Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0Lettura 0=0Lettura 1=0Lettura 2=0`

#### dc42

#13
##### Oct 28, 2013, 10:55 am
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

#14
##### Oct 28, 2013, 11:21 am
Nice, this is what I get:

Code: [Select]
`Lettura 0=45196Lettura 1=44724Lettura 2=23212Lettura 0=45196Lettura 1=44584Lettura 2=23232Lettura 0=45196Lettura 1=44748Lettura 2=23208Lettura 0=45200Lettura 1=44584Lettura 2=23212Lettura 0=45200Lettura 1=44724Lettura 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