atmega 328 ignition control

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello! I'm working on a ignition control for a small two strokes engine. The stock CDI unit is fixed advance, so i put my mcu between the crank sensor, and the CDI unit, delaying the signal as needed.
External interrupt 0 is triggered 1 time per revolution, about 30 degrees before TDC
mcu is atMega 328 p running at 16 Mhz

Here is the code:

ISR(INT0_vect){
  
 revolutionTicks = (revolutionOverflows << 16) + TCNT1;   //compute total ticks occurred in 1 revolution
 oneSectorTicks =  revolutionTicks >> 9;//divide the total revoultion ticks in 512 sectors
 ignitionDelayTicks = (oneSectorTicks * sparkDelaySectors);  //number of ticks to delay after the tach pulse for ignition
 revolutionOverflows = 0;//reset overflows counter
 TCNT1 = 0;//reset timer 1 counter
 OCR1B = ignitionDelayTicks;//set OCR1B
 sbi(TIFR1,OCF1B);   //clear B channel flag
 sbi(TIMSK1,OCIE1B);// enable timer compare interrupt B
  }


  
ISR(TIMER1_COMPB_vect){ 
 switch(ignitionStep){
  
  case 0:    //start ignition pulse
  sbi(PORTB,1);// pin PB1 spark!
  ignitionStep = 1;
  OCR1B = TCNT1 + ignPulseDuration;  //set OCR1B second step
  break;

  case 1:  //end of ignition pulse
  cbi(PORTB,1);//digital pin 9 off
  cbi(TIMSK1,OCIE1B);// disable timer compare interrupt B
  ignitionStep = 0;//reset ignitionstep for the next spark event
   break;}
}
 
 ISR(TIMER1_OVF_vect){
  revolutionOverflows ++;//increment overflows counter
   }

I tested the code running with a strobe light, it works fine, except a jitter occurs every time the OVF vector happens at the same time with the crank interrupt.
Probably, when the OVF vector happens when inside the INT0 ISR, the overflow counter is not updated.
Any suggestion?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You should use the input capture feature rather than an external interrupt. This will give you more precise timing. As well, if you enable the compare to take care of the port bit control, this too will give more precise timing and less code to execute in the isr. Ignore the timer overflow but ensure that at the lowest rpm you can measure using 65535 timer counts. As well, do not clear the timer.

Regarding the jitter, yes, you are probably correct with the overflow problem. If you get an overflow whilst in your external interrupt your calculations wont't work - you need to test the overflow interupt flag in the external interrupt isr and increment the overflow count if it is set, then clear the overflow interrupt flag by writing a '1' to it. That will stop the overflow isr being called when you exit the current isr. You can avoid all of this by following my suggestions above.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you considered changing your timer prescaler so that timer overflows do not need to be considered (even at the lowest RPM) ?

Let's say that your RPM range is 600 to 6000RPM. At 600RPM, this is 10RPS, or 100mS per rotation. If we take 100mS/65535 we get a time of about 1.5uS for a timer tick before we have to worry about timer overflow. If we set the timer tick to 2uS, we can be sure that overflows won't need to be considered at low RPMs when "running". Starting is a different matter, and would be considered separately.

At 6000RPM, this is 100RPS, or 10mS per rotation. At this speed each degree of crank rotation is equal to 10mS/360 = 27.78uS. With a 2uS timer resolution, this is better than 0.1 degree crank resolution for timing. Wouldn't this be good enough ?

You can also write assembly for your interrupt handler. This would include pushing and popping a minimal number of registers (2). If you used GPIO0 for your overflow counter, it might look something like this.

push   SREG (2)
push   R16; (2)
in     R16,GPIO0; (1)
inc    R16; (1)
out    GPIO0,R16; (1)
pop    R16; (2)
pop    SREG; (2)

This would get you in and out in less than 1uS. Still "some" jitter, but I suspect it's not nearly as bad as you have now.