Pages: [1] 2   Go Down
Author Topic: Uneven respone to interrupt command  (Read 1380 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
I am struggeling with the accuracy of interrupt in Duemilanove 328. What I try to do is to get a quick and even response to an interrupt getting a HIGH to pin13.

Here is my issue: I am pushing a 5V signal into a interrupt 0 and want to get a immediate response. The acuracy shall be below +/-4 microseconds.

Below is the code I used.

I used the command "attachInterrupt(0, ignite, RISING);" command to put the LEDpin to high. I attached an oscilloscope screenshot of  showing the outcome. The interrupt trigger is very much left. The next rising signal (response1) is the response to it I normally get- which would be fine. But for whatever reason there is sometimes a response (response2) which is 20 micros delayed- and only 20 micros..not more and not less. This photo contains both response signals because I used a long exposure time with my camera to get some kind of overlaying signals.

Here is my question. How can I avoid this irregular response?


Code:
#include <EEPROM.h>
#include <avr/pgmspace.h>

int maps[4][23] = {
     {110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, 7500     } 
  ,  {310, 300, 290, 280, 270, 260, 250, 240, 230, 220, 210, 200, 190, 180, 170, 160, 150, 140, 130, 120, 110, 100, 7500} 
  ,  {410, 400, 390, 380, 370, 360, 350, 340, 330, 320, 310, 300, 290, 280, 270, 260, 250, 240, 230, 220, 210, 200, 7500} 
  ,  {510, 500, 490, 480, 470, 460, 450, 440, 430, 420, 410, 400, 390, 380, 370, 360, 350, 340, 330, 320, 310, 300, 7500} 
};

int lowgreenrpm=500;   
int lowyellowrpm=3000;   
int lowredrpm=4000;   

int dwell=2500;

//Shift register
#include <Shifter.h>
#define SER_Pin 5 //SER_IN
#define RCLK_Pin 4 //L_CLOCK
#define SRCLK_Pin 3 //CLOCK
#define NUM_REGISTERS 1 //how many registers are in the chain
Shifter shifter(SER_Pin, RCLK_Pin, SRCLK_Pin, NUM_REGISTERS);
//Shift register


//LCD
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

unsigned long refresh=0;         
unsigned long refreshold=0;     
unsigned long refreshtarget=500; 
//LCD


#define FASTADC 1
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

//Analoge Eingänge
int MAPPin=A0;         //Map Selektor
int baseZZPPotiPin=A1;
int EGTPin=A2;
int IATPin=A3;     

int baseZZPPin = 6; 
int LEDpin=13;              //Input trigger auf Board LED zeigen


//Verschiedene Variablen
int baseZZPPinstat;
int baseZZPPinstatold;
int EEPROMval;
int EGTval=0;         
int IATval=0;         
int MAPval;   
int z=1;
unsigned long delaycalc;
unsigned long mi;


unsigned long microshigh1; 
unsigned long microshigh2; 
float deltamicros; 
int delayignitionmicros=0;

int LEDbaseign = 13;
int LEDtrigger=11;

int RPMsteps[] = {  500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500,8000,8500,9000,10000,10500,11000};



int RPM;   //gemittelter Wert
int RPM1;  //Erster Wert der Mittelung
int RPM2;  //Zweiter Wert der Mittelung
float RPM3;  //Dritter Wert der Mittelung

int ignlow;
int ignhigh;
int retardlow;
int retardhigh;
float zzp;
float delayzzp;       
float baseZZP;       
float baseZZPadjust; 

void setup()
{
  Serial.begin(115200);

#if FASTADC
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
#endif

  pinMode(baseZZPPin, INPUT);
  pinMode(LEDpin, OUTPUT);
 
  attachInterrupt(0, ignite, RISING);

  baseZZP=250;
  baseZZPadjust=EEPROM.read(1); //Korrektur ZZP aus EEPROM
 
//LCD Displaygrösse definieren 
  lcd.begin(16, 2);

  shifter.clear(); //set all pins on the shift register chain to LOW
  shifter.write(); //send changes to the chain and display them
}

void loop(){
 
external();  //lcd1();   //LCD Display Funktion Aktivierung
}

void ignite()
{
 
 mi=micros();


  digitalWrite(LEDpin, HIGH);//für LED
  microshigh2 = micros();
  deltamicros= microshigh2-microshigh1;
  digitalWrite(LEDpin, LOW);//für LED
  zzpcalc(); //Starte Berechnung des ZZP sofort nach Zündung
}

void lcd1()
{
}




void zzpcalc(){
//MAPval=1;
  //Errechnung der Drehzahl aus deltamicros und Mittelung der Drehzahl über die letzten 3 Werte
  RPM1=RPM2;
  RPM2=RPM3;
  RPM3=((1/ deltamicros)*60000000.0);   //Errechnung aktuellen der Drehzahl
  RPM= (RPM1+RPM2+RPM3)/3;           //Gemittelte Drehzahl zur weiteren Benutzung
  if (RPM3<100) (RPM=500);  //Falls bei der ersten Umdrehung noch keine Drehzahl errechnet werden kann oder 0 ist werden 500RPM angenommen
  microshigh1=  microshigh2; //Alten Wert in Buffer legen

  // Feststellen welche Spalte der ZZP und DrehzahlMap ausgelesen wird
  ignlow= (RPM/500)-1;
  ignhigh=ignlow+1;
  retardlow = maps[MAPval][ignlow]; //unterer ZZP Datenpunkt der Map zur interpolation
  retardhigh = maps[MAPval][ignhigh]; //oberer ZZP Datenpunkt der Map zur interpolation

if (RPM<500) {
retardlow=100;
retardhigh=100;
}

  zzp=((((float)retardlow +((float)RPM-RPMsteps[ignlow])*(((float)retardhigh-(float)retardlow)/((float)RPMsteps[ignhigh]-(float)RPMsteps[ignlow])))));//Lineare Interpolation//
  delayzzp= (deltamicros/360.00)*((baseZZP-(float)baseZZPadjust-(float)zzp)/10.0); // Erechnung des zeitlichen Verzögerung der Zündauslösung inklusive des Basiszündzeitpunkts
 
if (RPM>(maps[MAPval][22])) (delayzzp=100000); //Drehzahlbegrenzer: Zündung wird in einen Bereich verschoben, der später als der nächste Trigger liegt

if (baseZZPPinstat==HIGH){
  delayzzp= (deltamicros/360.00)*((baseZZP-(float)baseZZPadjust)/10); // Erechnung des zeitlichen Verzögerung der Zündauslösung inklusive des Basiszündzeitpunkts
}

}

void external(){ 
  if (z==1)   //einlesen EGT
  { 
EGTval= analogRead(EGTPin);
EGTval= EGTval/20;
  }


  if (z==2)   //Analogread Map (mit Zuweisung der Map)
 
 
  {   
    MAPval= analogRead(MAPPin);
if   ((MAPval>0) && (MAPval<256)) MAPval=0;
if   ((MAPval>257) && (MAPval<512)) MAPval=1;
if   ((MAPval>513) && (MAPval<767)) MAPval=2;
if   ((MAPval>768) && (MAPval<1024)) MAPval=3;

     }



  if (z==3)   //lese IAT und errechne Temperatur
  { 
 
IATval= analogRead(IATPin);  //WERT= 850
IATval= 731;
if (IATval>=466)(IATval=-0.1124*IATval+102.3);
if (IATval<466)(IATval=0.006*(IATval*IATval)-0.5229*IATval+169.35);

  }

  //***************************************************************
  //Schaltanzeige
  //***************************************************************
  if (z==4)   //DigitalRead für Displayumschaltung
  { 
    if (RPM > lowgreenrpm){
      if (RPM < lowyellowrpm)
      {
        shifter.setPin(0, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(1, HIGH); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(2, HIGH); //set pin 1 in the chain(second pin) HIGH
        shifter.write(); //send changes to the chain and display them
      }

      if (RPM > lowyellowrpm)
      {
        shifter.setPin(0, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(1, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(2, HIGH); //set pin 1 in the chain(second pin) HIGH
        shifter.write(); //send changes to the chain and display them
      }


      if (RPM > lowredrpm)
      {
        shifter.setPin(0, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(1, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.setPin(2, LOW); //set pin 1 in the chain(second pin) HIGH
        shifter.write(); //send changes to the chain and display them
      }
    }
    else
    {
      shifter.setPin(0, HIGH); //set pin 1 in the chain(second pin) HIGH
      shifter.setPin(1, HIGH); //set pin 1 in the chain(second pin) HIGH
      shifter.setPin(2, HIGH); //set pin 1 in the chain(second pin) HIGH
      shifter.write(); //send changes to the chain and display them
    }

  }
  if (z==5)   //Basiszündzeitpunkt Funktion
  { 
    baseZZPPinstat= digitalRead(baseZZPPin); //Auslesen des Schalters zum einstellen des BasisZZP
    if (baseZZPPinstat==HIGH) basezzp(); //basezzp einstellen
   
    if (baseZZPPinstat != baseZZPPinstatold)
    { 
      EEPROM.write(1, (baseZZPadjust));  //Schreibt Wert in den nichtflüchtigen Speicher wenn Schalter wieder umgelegt wird
      baseZZPPinstatold=LOW;
      baseZZPadjust=EEPROM.read(1);
    }
    baseZZPPinstatold= baseZZPPinstat;
  }

  // Zähler um die obigen Funktionen abwechselnd auszuführen
  if (z<7) z=z+1;
  if (z>6)  z=1;
  microshigh2 = microshigh1;  //wichtig um die ZZP zu berechnen
}

// Einstellung der Zündung ohne Schwungscheibe abzunehmen (Basezzp)
void basezzp(){ 
  baseZZPadjust= analogRead(baseZZPPotiPin); //angenommen wird ein Verstellbereich von 0 bis 10° KW
  baseZZPadjust= map(baseZZPadjust, 0, 1024, 0, 100);
}



* Neues Bild (1).png (1208.04 KB, 748x811 - viewed 40 times.)
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Interrupt latency is dependant on many things, not the least of which is the fact that another interrupt may be being handled when your trigger arrives.

I don't know that you can do better than that on an Arduino, I haven't looked at your code yet, can you disable the timer interrupt being used for millis()?

If you need a really fast response polling is faster.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3469
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The Arduino has usually the timer interrupt for timer 0 active. While this interrupt is processed your interrupt will be delayed. This is what you see with your scope. So what you need to do either stop these interrupts or change the libraries to allow interrupts while the timer interrupt is active. Both approaches have some sideeffects though.
Logged

Check out my experiments http://blog.blinkenlight.net

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In my code I am using the Interrupt0. Do you think it helps using another Interrupt port?
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

It doesn't matter which interrupt you use, the problem is the same.

Do you need to do anything else while waiting for this trigger?

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes I do something else between the interrupts (including short analog read and several other calculations, LCD submission...). Pls see my code under "external()".
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes I see.

Couple of things,

Code:
void ignite()

  mi=micros();
  digitalWrite(LEDpin, HIGH);//für LED
  microshigh2 = micros();
  deltamicros= microshigh2-microshigh1;
  digitalWrite(LEDpin, LOW);//für LED
  zzpcalc(); //Starte Berechnung des ZZP sofort nach Zündung
}

Don't use digitalWrite(), you can save a couple of uS by directly writing to the port.

Don't use attachInterrupt(), you can save a few more uS by writing your own ISR because the default handler does a test to see if a function has been attached.

So your code may look like this (totally untested)...

Code:
...
float volatile deltamicros;  // note volatile added, BTW why is this a float?
boolean volatile do_calc = false;
...

ISR (INT0_vect) {
  mi = micros();
  PORTB != (1 << PB5);
  microshigh2 = micros();
  deltamicros = microshigh2-microshigh1;
  PORTB &= ~(1 << PB5);
 
   // never call an large function from an ISR, set a flag and call it from loop()
  do_calc = true;
}


void setup() {
   ...
}

void loop() {


  if (do_calc) {
    zzpcalc();
    do_calc = false;
  }

  external();

}

You are using micros() which IIRC in turn needs timer0 to be running and that will cause some jitter for the reasons we stated above.
It may also be worth disabling the timer0 interrupt and getting your micros information directly from the timer hardware. But I haven't delved deeply into your logic so understand what's needed.
 
NOTE: Why is deltamicros a float, expecting fractional values?

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3469
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

What exactly is this coding going to accomplish? IMHO 4 microseconds = 64 cycles is plenty of time. However it would help a lot to understand what you are doing and what your constraints actually are. According to the comments it looks like some kind of combustion engine control. But then again I could be wrong.

With regard to the timing: depending on how far you are going to push it even 1 microseconds = 16 cycles latency is not to hard to achieve. For some very specific project I once implemented something with a guaranteed latency of 8 cycles. Unless it is clear what you really need it stays unclear if you need techniques similar to what I did or if something much simpler might suffice (simpler == much easier to debug).
Logged

Check out my experiments http://blog.blinkenlight.net

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Udo,
I like your approach (without dislike of the other helpful forum member!).
What I try to achieve is to get a 5V trigger signal from outside and kick a 5V signal out after a certain time. I think from my code using functions like "ignite" its clear that this will be an engine ignition.  I receive a trigger from Flywheel and kick the sinusoidal 5V signal (rectangularized by a comparator) out with a certain delay. I will use the "while" command to execute a delay without using "delay()" command.
The tests I have done shown on the osicilloscope where done without any delay function but only immediate putting the PIN to HIGH only.

 
The only thing I need is a consistent execution of ISR...not necessarily a quick one. I need to trust this execution time is correct without jumping around 20microseconds

« Last Edit: September 04, 2012, 07:36:47 am by soulid » Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
kick the sinusoidal 5V signal (rectangularized by a comparator) out
This I don't understand, where does the sine wave come from? The Arduino can't produce one and even if it could if you are squaring it anyway there's no point.

Quote
with a certain delay.
Can this delay be calculated before you need the pulse? IE from the previous cycle.

If so you can poll the input trigger then do your own while loop to get the delay and another to time the pulse. Then when that's done do all the calculations for the next cycle.

This will have just a uS or two of latency and jitter.

And I repeat, why does anything to do with micros need a float variable.

______
Rob

Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Graynomad,
sinusoidal signal is coming from a inductive trigger "looking" to the flywheel

I calculated this delay AFTER the ignite to reduce any unforeseeable time delay.
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I calculated this delay AFTER the ignite
Then the processor has nothing better to do so poll the trigger input (much faster than interrupts) and do two loops to time the delay and pulse width.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3469
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@soulid: I understand your point that you want consistent responses. However you will always pick up some jitter, for example while digitizing the analog signal. What I am aiming at: what is the maximum RPM of you engine and what angular resolution do you really need.

Example: suppose max RPM = 12000 RPM and angular resolution ~ 1°. Then resolution in clock cycles = 16 000 000 * 60 / (12 000 * 360) = 222 ticks (or 13 microseconds). --> You better be precise to ~5 microseconds.

With regard to consistent responses. Turn of all interrupts the Arduino provides by default. Then switch on only what you ***really*** need. Actually as it was already proposed you can do this without any interrupts at all.

However since you need to do some other stuff at the same time this might not be an option. I can think of several solutions:

1) Disable all other interrupts but the interrupt for the signal you want to delay. Process the delay in your interrupt routine and then go back to the main program. It follows that you main program must not rely on any timing functions and must not lock interrupts for more than say << 10 microseconds

2) Same as above but instead of a delay enable an additional interrupt in the timer code and set up a timer interrupt for the ouput.

3) Same as (1) but instead of generating the output in software use the PWM hardware to generate just this single pulse. Take care to disable the PWM early enough.


There are some mandatory actions here:
a) Read the datasheet on interrupts, especially timer interrupts.
b) Analyze the Arduino interrupt setup code (not to much and not to hard to understand). Do not forget the interrupts caused by communications
c) Analyze the LCD libraries if it has anything timing critical that might block interrupts.
Logged

Check out my experiments http://blog.blinkenlight.net

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8471
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
However you will always pick up some jitter,
He could always use an LPC ARM, they can be set to have jitter-free interrupts smiley

At 12000RPM he has 5mS between pulses and the pulses don't look to be very long so most of that time is spent waiting for the next pulse or, writing to an LCD or calculating a pulse width.

LCDs are slow but I think if the data for it is put in a ring buffer and for example one byte sent every in inter-pulse period that shouldn't get in the way.

I'm thinking pseudo code like this

loop {
  wait for trigger
  do pulse
  calc value
  write stuff to LCD ring buffer if necessary
  read a byte from ring buffer and send to LCD
}

No interrupts required (and disable any default interrupts) and "wait for trigger" is a tight polling loop.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

0
Offline Offline
Full Member
***
Karma: 1
Posts: 140
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That does not work for me. Think about:

1) Analog read with a prescaler set to 16: 104 microseconds (and I have two of them)
2) LCD write with 4bit: takes more than 20 microseconds as well

Both single functoins are far to slow to "wait" for the trigger

What I did was the following

Code:
Pseudocode


setup
attachInterrupt

loop
{
do-other-things
}

function Interrupt action
put PIN13->high
jump to calculate_only_once_after_interrupt


function calculate_only_once_after_interrupt
calculatecalculatecalculatecalculatecalculate


function do-other-things
x=x+1
if x==8->x=1

if x=1
If the last LCD write was more than 25mS away-> write LCD
if x=2
read analog1

if x=2
read analog2

.
.
.

if x=8
write Pinxyz HIGH

The function do-other-things will lead to do one action out of 8 when called. That ensures proper execution of each function.
Logged

Pages: [1] 2   Go Up
Jump to: