Go Down

### Topic: Driving Comp Air Engine with Solenoids and Arduino (Read 19050 times)previous topic - next topic

#### Newman180

#60
##### Dec 06, 2010, 06:00 amLast Edit: Dec 06, 2010, 06:21 am by Newman180 Reason: 1

I completely understand taking the TDC time and storing it. I also agree with and understand the method of using the values of 0001 to 0999 instead of degrees.

You mention to multiply these numbers by the time between tdc, but I don't see how that works.

Its not clicking with me on how to take the time per revolution and find a position with that. I want to say you take the time (ex 500) and divide it by the total revolution 1000, giving a ratio of where its at, but every time the RPM's change the ratio will be off.

Do I make sense?

Here's the code with a few additions.
Code: [Select]
`//----------------------------------------------- volatile unsigned int eventcount; unsigned int tdc; // Rotation Time unsigned int rpm; // RPM unsigned long timeold; // Previous time Hall Sensor Picked up const int So17Pin = 3; // Solenoids 1 and 7 assigned to Pin 3 const int So35Pin = 4; // Solenoids 3 and 5 assigned to Pin 4 const int So28Pin = 5; // Solenoids 2 and 8 assigned to Pin 5 const int So46Pin = 6; // Solenoids 4 and 6 assigned to Pin 6 const int buttonPin = 13; // Button For Switching "Valve Open Time" Percent const int button2Pin = 14; // Button For Start Mode Intake 1+7 const int button3Pin = 15; // Button For Start Mode Intake 3+5 int buttonPushCounter = 0;   // counter for the number of button presses--- used with button for "Valve Open Time" Percent int buttonState = 0;         // current state of the button int button2State = 0;        // current state of the button 2 int button3State = 0;        // current state of  the button 3 int lastButtonState = 0;     // previous state of the button int lastButton2State = 0;     // previous state of the button 2 int lastButton3State = 0;     // previous state of the button 3 void setup() {   Serial.begin(9600); // Communicate to Serial Port   pinMode (So17Pin, OUTPUT); // Solenoid 1+7 as Output   pinMode (So35Pin, OUTPUT); // Solenoid 3+5 as Output   pinMode (So28Pin, OUTPUT); // Solenoid 2+8 as Output   pinMode (So46Pin, OUTPUT); // Solenoid 4+6 as Output   pinMode(buttonPin, INPUT); // Button for "Valve Open Time" as Input   pinMode(button2Pin, INPUT); // Button For Start Mode 1+7 Intake   pinMode(button3Pin, INPUT); // Button For Start Mode 3+5 Intake   attachInterrupt(0, event_count, RISING); // Interrupt 0 is Pin 2 Hall Effect Sensor }  void loop()  {    detachInterrupt(0);    if (eventcount >= 1) {   // Calculate position of piston    tdc = ((micros() - timeold)/ eventcount); // Time Between Each Hall Pulse    timeold = micros();    eventcount = 0;  }{   }  // Open for 100% Stroke Intake (1 and 7) Exhaust (4 and 6)  if () // If the piston is inbetween 1 to 179 degrees of revolution then...  {     digitalWrite(So17Pin, HIGH); // Fire Solenoid 1 and 7 (Intake)     digitalWrite(So46Pin, HIGH); // Fire Solenoid 4 and 6 (Exhaust)     Serial.println("Intake (1,7) Exhaust (4,6) Fired 100%"); // Print Status and Percent of Valve Open     }  }else {  digitalWrite(So17Pin, LOW); // Don't Fire Solenoid 1 and 7 (Intake)  digitalWrite(So46Pin, LOW); // Don't Fire Solenoid 4 and 6 (Exhaust)  }  // Open for 100% Stroke Intake (3 and 5) Exhaust (2 and 8)  if () // If the piston is inbetween 181 to 359 degrees of revolution then...  {     digitalWrite(So35Pin, HIGH); // Fire Solenoid 3 and 5 (Intake)     digitalWrite(So28Pin, HIGH); // Fire Solenoid 2 and 8 (Exhaust)     Serial.println("Intake (3,5) Exhaust (2,8) Fired 100%"); // Print Status and Percent of Valve Open  }   }else {  digitalWrite(So35Pin, LOW); // Don't  Fire Solenoid 3 and 5 (Intake)   digitalWrite(So28Pin, LOW); // Don't Fire Solenoid 2 and 8 (Exhaust)}}attachInterrupt(0, event_count, RISING);}`

#### Newman180

#61
##### Dec 06, 2010, 06:20 am
Actually wouldn't it be (Time between TDC/1000) (position demanded)?

That sounds right.

Once again, how do I make the "if" statement? I.E if the piston is at 0250 do this...

#### PaulS

#62
##### Dec 06, 2010, 01:41 pm
At the start of loop(), you detach interrupts. At the end of loop(), you reattach them. The only time for an interrupt to be fired is between the end of one iteration of loop, and the beginning of the next. As you might imagine, that's an awfully short time.

tdc does not stand for top-dead-center as you are calculating a value. It is the time required to complete one revolution at the current speed.

This last posting does not contain the actual interrupt handler, so it is not possible to determine if you record WHEN the interrupt fired (when TDC occurred).

Absent that knowledge, you have no idea when anything else needs to happen, because you have no point of reference.

#### Newman180

#63
##### Dec 06, 2010, 03:11 pm
Quote
At the start of loop(), you detach interrupts. At the end of loop(), you reattach them. The only time for an interrupt to be fired is between the end of one iteration of loop, and the beginning of the next. As you might imagine, that's an awfully short time.

Agreed. Is there a way to give more time time for my interrupt? I don't think a delay would be smart. If then engine is spinning at 2000 rpm that's 30,000 us between each pulse. How much time does the arduino need?

Quote
This last posting does not contain the actual interrupt handler, so it is not possible to determine if you record WHEN the interrupt fired (when TDC occurred).

Isn't this the "handler"?
Code: [Select]
` tdc = ((micros() - timeold)/ eventcount); // Time Between Each Hall Pulse    timeold = micros();    eventcount = 0;`
The latest tdc minus the last tdc= time it took to hit tdc the latest time, right?

If that's not right, can you give a little bit more detailed example on said handler, and what I can do to give myself more time for the interrupts.

#### Newman180

#64
##### Dec 06, 2010, 03:27 pm
Quote
Why would we ever want to detach the interrupt?
It seems to me like the interrupt handler should simply record the "time" (in microseconds) of TDC.
Then on each trip through the loop you look at the loop time and see whether you need to open or close something.

I thought thats what you guys meant when saying "recording the time"

If I delete the interrupts, it gets rid of the mess Paul was talking about, which could be handy.

#### PaulS

#65
##### Dec 06, 2010, 03:29 pm
Quote
Isn't this the "handler"?

No. You have an attachInterrupt statement. It says "call this function (the handler) when someConditionIsTrue". It's that code (event_count()) that we need to see.

Quote
what I can do to give myself more time for the interrupts.

Start by asking yourself why you are disabling interrupts at all. The answer to that is that any non-byte-sized value affected (written to) by the interrupt handlers (you only have one) could be affected (written to) by the interrupt service routine while you are in the middle of resetting it. To prevent this, you temporarily disable interrupts.

As soon as the reset of eventcount is complete (along with any other values affected by the interrupt handler(s), you should enable interrupts again.

#### Newman180

#66
##### Dec 06, 2010, 03:38 pm
Quote
As soon as the reset of eventcount is complete (along with any other values affected by the interrupt handler(s), you should enable interrupts again.

Ok. Like this?
Code: [Select]
`void setup() {   Serial.begin(9600); // Communicate to Serial Port   pinMode (So17Pin, OUTPUT); // Solenoid 1+7 as Output   pinMode (So35Pin, OUTPUT); // Solenoid 3+5 as Output   pinMode (So28Pin, OUTPUT); // Solenoid 2+8 as Output   pinMode (So46Pin, OUTPUT); // Solenoid 4+6 as Output   pinMode(buttonPin, INPUT); // Button for "Valve Open Time" as Input   pinMode(button2Pin, INPUT); // Button For Start Mode 1+7 Intake   pinMode(button3Pin, INPUT); // Button For Start Mode 3+5 Intake   attachInterrupt(0, event_count, RISING); // Interrupt 0 is Pin 2 Hall Effect Sensor }  void loop()  {    detachInterrupt(0);    if (eventcount >= 1) {   // Calculate position of piston    tdc = ((micros() - timeold)/ eventcount); // Time Between Each Hall Pulse    timeold = micros();    eventcount = 0;  }attachInterrupt(0, event_count, RISING);{   }  // Open for 100% Stroke Intake (1 and 7) Exhaust (4 and 6)  if () // If the piston is inbetween 1 to 179 degrees of revolution then...  {     digitalWrite(So17Pin, HIGH); // Fire Solenoid 1 and 7 (Intake)     digitalWrite(So46Pin, HIGH); // Fire Solenoid 4 and 6 (Exhaust)     Serial.println("Intake (1,7) Exhaust (4,6) Fired 100%"); // Print Status and Percent of Valve Open     }  }else {  digitalWrite(So17Pin, LOW); // Don't Fire Solenoid 1 and 7 (Intake)  digitalWrite(So46Pin, LOW); // Don't Fire Solenoid 4 and 6 (Exhaust)  }  // Open for 100% Stroke Intake (3 and 5) Exhaust (2 and 8)  if () // If the piston is inbetween 181 to 359 degrees of revolution then...  {     digitalWrite(So35Pin, HIGH); // Fire Solenoid 3 and 5 (Intake)     digitalWrite(So28Pin, HIGH); // Fire Solenoid 2 and 8 (Exhaust)     Serial.println("Intake (3,5) Exhaust (2,8) Fired 100%"); // Print Status and Percent of Valve Open  }   }else {  digitalWrite(So35Pin, LOW); // Don't  Fire Solenoid 3 and 5 (Intake)   digitalWrite(So28Pin, LOW); // Don't Fire Solenoid 2 and 8 (Exhaust)}}}`

Quote
No. You have an attachInterrupt statement. It says "call this function (the handler) when someConditionIsTrue". It's that code (event_count()) that we need to see.

Ahh. I had it before, and forgot it this time. I didn't really know what it did. You mean this?
Code: [Select]
`  void rpm_count() {   eventcount++;   //Updated Count }void loop()  {    detachInterrupt(0);    if (eventcount >= 1) {   // Calculate position of piston    tdc = ((micros() - timeold)/ eventcount); // Time Between Each Hall Pulse    timeold = micros();    eventcount = 0;  }attachInterrupt(0, event_count, RISING);{   }  // Open for 100% Stroke Intake (1 and 7) Exhaust (4 and 6)`

#### PaulS

#67
##### Dec 06, 2010, 03:51 pm
Quote
Ahh. I had it before, and forgot it this time. I didn't really know what it did. You mean this?
Code

No.

You have this statement:
Quote
attachInterrupt(0, event_count, RISING);

It says "When the appropriate pin (pin 2) transitions to RISING, call event_count()".

So, what/where is event_count()?

#### Newman180

#68
##### Dec 06, 2010, 06:59 pm
Quote
It says "When the appropriate pin (pin 2) transitions to RISING, call event_count()".

So, what/where is event_count()?

I think I understand, I had my labels mixed up.

Code: [Select]
` void event_count() {   eventcount++;   //Updated Count }void loop()  {    detachInterrupt(0);    if (eventcount >= 1) {   // Calculate position of piston    tdc = ((micros() - timeold)/ eventcount); // Time Between Each Hall Pulse    timeold = micros();    eventcount = 0;  }attachInterrupt(0, event_count, RISING);{   }  // Open for 100% Stroke Intake (1 and 7) Exhaust (4 and 6)`

void event_count()
{
eventcount++;
//Updated Count

This is declaring that event_count = eventcount  Right?

#### Newman180

#69
##### Dec 06, 2010, 09:36 pm
Quote
If I were doing this, I would want to actually RECORD the TIME (in microseconds) when the TDC interrupt was received. Else, we are only recording the next time through the loop AFTER TDC.  That does not seem as accurate to me.

Code:

Do you mean void loop?  I.E
Code: [Select]
`void loop(){  detachInterrupt(0);  // disable interrupts while we handle the interrupt (!)  prevTDCtime = TDCtime;  // save last TDC  TDCtime = micros();  // save THIS TDC  revPeriod = TDCtime - prevTDCtime;  // calculate revolution period  //  // some code in here to handle micros() overflow  //  attachInterrupt(0, event_count, RISING);  // re-enable the interrupt now that we are done} `

#### Newman180

#70
##### Dec 07, 2010, 05:36 am
Quote
No. That is exactly what I mean is wrong.  That does NOT belong in the loop()  It was not an accident that I wrote it as a standalone function, OUTSIDE the loop().

That is why we have interrupt handlers, to handle time-sensitive things, while the loop() simply goes round and round without any specific reference to when it begins and when it ends/restarts.

The interrupt presumably triggers when the TDC sensor goes high. AT THAT MOMENT, regardless of what is happening in loop(), we want to know the TIME of the TDC so that all subsequent activities inside the loop() are referenced to an exact time of TDC.

Ok that makes sense. This also means that the time-time old function in void loop is now no longer needed. Correct?

I'm looking into the overflow code, I have to read up on it later, and will update tomorrow.

#### Newman180

#71
##### Dec 07, 2010, 06:18 am
Quote

If you end up with some tremendously large number, you do a simple subtraction to restore it to "nominal".
If I weren't so tired this evening, I could remember what you subtract...

No worries. I was assuming you were referring to using timer0_overflow_count = 0;  at first, but found that the code is used for millis() not micros()
If you subtract the large number by a standard ratio of something over 1000 like a 10 to 1 ratio I.E 10000/1000  would it not bring it back into "perspective"?

I also found this
Quote
1.
unsigned integer math is immune to a single overflow:
example with 8 bit (uint8_t):
last time stamp: 253
current time stamp: 5
difference: 5-253 = -248 modulo 256 = 8

I don't know how it relates. Getting kinda tired though myself.

#### Newman180

#72
##### Dec 08, 2010, 01:10 am
I'm not having too much luck finding this overflow code that works with my application.

The micros() overflow at 71 minutes. If the engine ever takes that long to rotate once, somethings wrong. So if we reset prevTDCtime to zero after it has re enabled the interrupt, won't it work perfectly? Like so
Code: [Select]
`void event_count(){  detachInterrupt(0);  // disable interrupts while we handle the interrupt (!)  prevTDCtime = TDCtime;  // save last TDC  TDCtime = micros();  // save THIS TDC  revPeriod = TDCtime - prevTDCtime;  // calculate revolution period  attachInterrupt(0, event_count, RISING);  // re-enable the interrupt now that we are done  prevTDCtime = 0;} `

If not, have you remembered what the subtraction rule is?

Also, with the void event_count, if its not looping, how does the program know to go back through and detach interrupts and calculate ect... all over again? Or is that the purpose of the interrupt where its name actually means its definition; where when the interrupt signal is triggered it interrupts the code in progress goes back to that event_count code and then continues the loop?

#### PaulS

#73
##### Dec 08, 2010, 01:36 am
Quote
Or is that the purpose of the interrupt where its name actually means its definition; where when the interrupt signal is triggered it interrupts the code in progress goes back to that event_count code and then continues the loop?

Exactly. An interrupt is like a telephone ringing. You mark your spot in the book you are reading, answer the phone, tell the caller to get lost, and resume reading, right where you left off.

The output from micros() rolls over every 71 minutes. If, as you point out, it takes less than that to make one revolution, you can forget about issues with micros rolling over, as long as you only subtract earlier values from "now".

No, setting prevTDCTime to 0 after enabling interrupts again is not needed and not correct.

#### Newman180

#74
##### Dec 08, 2010, 01:44 am
Quote
Exactly. An interrupt is like a telephone ringing. You mark your spot in the book you are reading, answer the phone, tell the caller to get lost, and resume reading, right where you left off.

The output from micros() rolls over every 71 minutes. If, as you point out, it takes less than that to make one revolution, you can forget about issues with micros rolling over, as long as you only subtract earlier values from "now".

No, setting prevTDCTime to 0 after enabling interrupts again is not needed and not correct.

That telephone ring is a great example. Thank you.
And yes it will never ever take 71 minutes to make one revolution, and I am always subtracting the new TDC from the previous TDC

Here is how my code looks now.
Code: [Select]
`//----------------------------------------------- volatile unsigned int eventcount; unsigned int revperiod; // Rotation Time unsigned long prevTDCtime; // Previous time Hall Sensor Picked up unsigned long TDCtime; // const int So17Pin = 3; // Solenoids 1 and 7 assigned to Pin 3 const int So35Pin = 4; // Solenoids 3 and 5 assigned to Pin 4 const int So28Pin = 5; // Solenoids 2 and 8 assigned to Pin 5 const int So46Pin = 6; // Solenoids 4 and 6 assigned to Pin 6 const int buttonPin = 13; // Button For Switching "Valve Open Time" Percent const int button2Pin = 14; // Button For Start Mode Intake 1+7 const int button3Pin = 15; // Button For Start Mode Intake 3+5 int buttonPushCounter = 0;   // counter for the number of button presses--- used with button for "Valve Open Time" Percent int buttonState = 0;         // current state of the button int button2State = 0;        // current state of the button 2 int button3State = 0;        // current state of  the button 3 int lastButtonState = 0;     // previous state of the button int lastButton2State = 0;     // previous state of the button 2 int lastButton3State = 0;     // previous state of the button 3 void setup() {   Serial.begin(9600); // Communicate to Serial Port   pinMode (So17Pin, OUTPUT); // Solenoid 1+7 as Output   pinMode (So35Pin, OUTPUT); // Solenoid 3+5 as Output   pinMode (So28Pin, OUTPUT); // Solenoid 2+8 as Output   pinMode (So46Pin, OUTPUT); // Solenoid 4+6 as Output   pinMode(buttonPin, INPUT); // Button for "Valve Open Time" as Input   pinMode(button2Pin, INPUT); // Button For Start Mode 1+7 Intake   pinMode(button3Pin, INPUT); // Button For Start Mode 3+5 Intake   attachInterrupt(0, event_count, RISING); // Interrupt 0 is Pin 2 Hall Effect Sensor } void event_count(){  detachInterrupt(0);  // disable interrupts while we handle the interrupt  prevTDCtime = TDCtime;  // save last TDC  TDCtime = micros();  // save THIS TDC  revPeriod = TDCtime - prevTDCtime;  // calculate revolution period  attachInterrupt(0, event_count, RISING);  // re-enable the interrupt now that we are done}  void loop() {   //-------------------------------------------------------------------------------------- START MODE    if (revperiod == 0)  // If Device is not spinning then Enable the Button's 2 and 3. If it is spinning Disable the Button's 2 and 3    { Serial.println("Device Immobile");    if (button2State != lastButton2State) // Start Mode Button2 Pressed--- Intake 1 and 7 Exhaust 4 and 6    {        if (button2State == HIGH) {     digitalWrite(So17Pin, HIGH); // Fire Intake 1 and 7     digitalWrite(So46Pin, HIGH); // Fire Exhaust 4 and  6     Serial.println("Start In 1+7"); // Display Via Serial "Start In 1+7"       }     }    {    if (button3State != lastButton3State) // Start Mode Button3 Pressed--- Intake 3 and 5 Exhaust 2 and 8    {    if (button3State == HIGH) {     digitalWrite(So35Pin, HIGH); // Fire Intake 3 and 5     digitalWrite(So28Pin, HIGH); // Fire Exhaust 2 and 8     Serial.println("Start In 3+5"); // Display Via Serial "Start In 1+7"       }     }    }   //------------------------------------------------------------------------------------ RUN MODE     {   }  // Open for 100% Stroke Intake (1 and 7) Exhaust (4 and 6)  if () // If the piston is inbetween 1 to 179 degrees of revolution then...  {     digitalWrite(So17Pin, HIGH); // Fire Solenoid 1 and 7 (Intake)     digitalWrite(So46Pin, HIGH); // Fire Solenoid 4 and 6 (Exhaust)     Serial.println("Intake (1,7) Exhaust (4,6) Fired 100%"); // Print Status and Percent of Valve Open     }  }else {  digitalWrite(So17Pin, LOW); // Don't Fire Solenoid 1 and 7 (Intake)  digitalWrite(So46Pin, LOW); // Don't Fire Solenoid 4 and 6 (Exhaust)  }  // Open for 100% Stroke Intake (3 and 5) Exhaust (2 and 8)  if () // If the piston is inbetween 181 to 359 degrees of revolution then...  {     digitalWrite(So35Pin, HIGH); // Fire Solenoid 3 and 5 (Intake)     digitalWrite(So28Pin, HIGH); // Fire Solenoid 2 and 8 (Exhaust)     Serial.println("Intake (3,5) Exhaust (2,8) Fired 100%"); // Print Status and Percent of Valve Open  }   }else {  digitalWrite(So35Pin, LOW); // Don't  Fire Solenoid 3 and 5 (Intake)   digitalWrite(So28Pin, LOW); // Don't Fire Solenoid 2 and 8 (Exhaust)}}}//-----------------------------------------------`

Since it would appear that all the timing issues cleared, I still have to get back to my misunderstanding of how to take the time and turn it into a number between 0001 and 0999.

Go Up