element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Ben Heck Featured Content
  • Challenges & Projects
  • element14 presents
  • element14's The Ben Heck Show
  • Ben Heck Featured Content
  • More
  • Cancel
Ben Heck Featured Content
Forum Engine Management
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Ben Heck Featured Content to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 63 replies
  • Subscribers 46 subscribers
  • Views 4146 views
  • Users 0 members are here
Related

Engine Management

jack.chaney56
jack.chaney56 over 9 years ago

Hi Ben,

I am a programmer with a very small amount of skill with circuits, and am looking to create a platform for an engine management system, using an Arduino Mega 2560. I had done a bit of the coding, when I ran into some timing issues with the built in Arduino manager, so I switched over and started using AVR Studio and a programmer to go directly to the chip itself.  The code looks like it should work ok, but now I need some additional circuits to handle the energy levels of coils and injectors (Something like IGBTs). Sensors are being run through simple dividers (no protection yet), and cam and crank inputs are through a simple comparitor

 

Let me know what you think,

Jack

  • Sign in to reply
  • Cancel

Top Replies

  • jack.chaney56
    jack.chaney56 over 7 years ago +2
    Back again... After a bit of time away seeking enlightenment (and a steady paycheck), I am ready to get back to work on my project. I have continued to play around with the code and a number of components…
  • jack.chaney56
    jack.chaney56 over 7 years ago +2
    I want to start this thing right, so the shopping list for people that want to play along at home: Raspberry Pi - version is not significant if you don't mind a slow response when using Eclipse, but 3B…
  • jack.chaney56
    jack.chaney56 over 7 years ago +2
    Start off with two things. First, I forgot (neglected) to provide instruction on how to get the compiled code onto the Nano. Fault of familiarity; having done the process so many times, I had shifted to…
Parents
  • jack.chaney56
    jack.chaney56 over 7 years ago

    Big one today as promised...

     

    First is types.h which hasn't changed particularly from the first. Just added some #define stuff at the end. There should be a device specific general .h file (usually the project name)

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
      
     This is the base include file, that pulls in all the AVR components, and standard items to make 
     life easier. I also declare some items for variable naming that makes it easier for me 
     to run on auto pilot.
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #ifndef _TYPES_H  
    #define _TYPES_H  
    #include <stdbool.h>  
    #include <stdio.h>  
    #include <avr/interrupt.h>  
    #include <avr/io.h>  
      
    typedef unsigned char  UByte;  
    typedef char           SByte;  
    typedef unsigned short UWord;  
    typedef short          SWord;  
    typedef unsigned long  ULong;  
    typedef long           SLong;  
      
    /* Hardware interfacers */  
    #define REG_B(a)      *((volatile UByte* const)(a))  
    #define REG_W(a)      *((volatile UWord* const)(a))  
    #define REG_L(a)      *((volatile ULong* const)(a))  
    #define REG_Bs(a)     *((volatile SByte* const)(a))  
    #define REG_Ws(a)     *((volatile SWord* const)(a))  
    #define REG_Ls(a)     *((volatile SLong* const)(a))  
    #define ARR_B(a)      ((UByte* const)(a))  
    #define ARR_W(a)      ((UWord* const)(a))  
    #define ARR_L(a)      ((ULong* const)(a))  
    #define ARR_Bs(a)     ((SByte* const)(a))  
    #define ARR_Ws(a)     ((SWord* const)(a))  
    #define ARR_Ls(a)     ((SLong* const)(a))  
      
    /* some use while(1) but I found this way compiles to fewer instructions */  
    #ifdef forever  
    #undef forever  
    #endif  
    #define forever    for(;;)  
      
    /* handy bits */  
    #define bit0    1  
    #define bit1    2  
    #define bit2    4  
    #define bit3    8  
    #define bit4    16  
    #define bit5    32  
    #define bit6    64  
    #define bit7    128  
    #define bit8    256  
    #define bit9    512  
    #define bit10    1024  
    #define bit11    2048  
    #define bit12    4096  
    #define bit13    8192  
    #define bit14    16384  
    #define bit15    32768  
      
    #define PART1    64L        /* part 1 of full circle for calculations */  
    #define PART2    512L       /* part 2 of full circle for calculations */  
    #define DEGS_PER_REV  (PART1*PART2)  
    #define TICS_PER_mS     2000  
    #define mS_PER_SEC      1000  
    #define SEC_PER_MIN     60  
    #define TICS_PER_MIN    (TICS_PER_mS*mS_PER_SEC*SEC_PER_MIN)  
    
    
    extern UWord etTkHi, etTkLo;  
    extern SWord dlyTkHi, dlyTkLo;  
    
    
    #endif  
    /* end of file */ 

     

    The second group are the files related to the hardware A2D.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    #define ADCSRA_INIT   (7<<ADPS0)     /* Right aligned Free running /64 prescaler */  
    #define A2D_CHAN   8                 /* number of A/D channels */  
    #define A2D_FILTER   8               /* 8 to 1 filter */  
    #define A2D_HALF   (A2D_FILTER/2)    /* used for rounding the result */  
      
    static UByte a2dSemi4;               /* pseudo semaphore, are updates being done */  
    static SWord a2dVal[A2D_CHAN];  
    static UByte a2dChan;                /* current converting A/D */  
      
    SWord getA2d(UByte v) { return (v < A2D_CHAN ? ((a2dVal[v] + A2D_HALF) / A2D_FILTER) : 0); }  
      
    void initA2D(void) {  
     a2dSemi4 = 0;                       /* pseudo semaphore, to only run the conversions when ready */  
     for (a2dChan = 0; a2dChan < A2D_CHAN; a2dChan++) {  
      a2dVal[a2dChan] = 0;               /* clear the buffer */  
     }  
     ADCSRA = ADCSRA_INIT;               /* configure the A/D */  
    }  
    void rtUpdateA2D(void) {  
     if (a2dSemi4 == 0) {                /* don't run if semaphore is active(already running) */  
      a2dSemi4 = 1;                      /* started, so set the semaphore on */  
      a2dChan = 0;                       /* start with the first A/D */  
      ADMUX = a2dChan;                   /* set the A/D channel */  
      ADCSRA |= (1 << ADEN);             /* enable the A/D channel */  
      ADCSRA |= (1 << ADSC);             /* start the A/D conversion */  
      ADCSRA |= (1 << ADIE);             /* turn on the interrupt */  
     }  
    }  
    ISR(ADC_vect) {  
     SWord tmp = ADC;                    /* conversion complete, so fetch the A/D value */  
     ADCSRA &= ~(1<<ADEN);               /* disable the A/D while changing the MUX */  
    /* the filtering is a running average... remove one part and add in the new as replacement */  
     a2dVal[a2dChan] += tmp - (a2dVal[a2dChan] / A2D_FILTER);  
     a2dChan++;                          /* point to the next channel */  
     if (a2dChan < A2D_CHAN) {           /* check to see if all the channels are done */  
      ADMUX = a2dChan;                   /* set the A/D channel */  
      ADCSRA |= (1 << ADEN);             /* enable the A/D channel */  
      ADCSRA |= (1 << ADSC);             /* start the A/D conversion */  
     } else {  
      ADCSRA &= ~(1 << ADIE);            /* all the channels read, so turn off the interrupt */  
      a2dSemi4 = 0;                      /* and clear the interrupt */  
     }  
    }  
    /* end of file */

     

    UART.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    #define UBAUD_57_6    34  
    #define UBAUD_38_4    51  
    #define UBAUD_19_2    103  
    #define UBAUD__9_6    207  
      
    #define BUFFSIZE      32  
      
    #define TXI0          (1<<TXCIE0)  
    #define UCSRA_INIT    (1<<U2X0)                           /* Timebase for UART 2x clock */  
    #define UCSRB_INIT    (1<<TXEN0)|(1<<RXEN0)|(1<<RXCIE0)   /* Enable transmit and receive lines and receive interrupt */  
    #define UCSRC_INIT    (3<<UCSZ00)|(0<<UPM00)|(0<<UMSEL00) /* n-8-1 */  
    #define COMMSPEED     UBAUD_19_2                          /* 19200 */  
      
    static UByte rxBuf[BUFFSIZE];  
    static UByte txBuf[BUFFSIZE];  
    static volatile UByte rxHed, rxTal, rxSiz;  
    static volatile UByte txHed, txTal, txSiz;  
      
    bool isComReady(void) { return (rxSiz != 0); }  
      
    void initUart(void) {  
     UCSR0A = UCSRA_INIT;  
     UCSR0B = UCSRB_INIT;  
     UCSR0C = UCSRC_INIT;  
     UBRR0 = COMMSPEED;  
     rxHed = 0; rxTal = 0; rxSiz = 0;  
     txHed = 0; txTal = 0; txSiz = 0;  
    }  
    /*==============================*/  
    /*       *** WARNING ***        */  
    /*  Do not use these functions  */  
    /*     within an interrupt      */  
    /*==============================*/  
    void putCom(UByte c) {  
     if ((UCSR0B & TXI0) == 0) {                              /* output buffer empty and no byte currently being sent */  
      UDR0 = c; UCSR0B |= TXI0;                               /* put byte in output port, and turn on the interrupt */  
     } else {  
      while (txSiz >= BUFFSIZE);                              /* make sure there is room in the buffer, or wait (see warning) */  
      cli();                                                  /* turn off interrupts, for safety */  
      txBuf[txTal] = c;                                       /* put the byte on the end of the buffer */  
      txTal = (txTal + 1) < BUFFSIZE ? txTal + 1 : 0;         /* adjust the pointer */  
      txSiz++;                                                /* increment the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     }  
    }  
    UByte getCom(void) {  
     UByte c = 0;  
     if (rxSiz > 0) {                                         /* if there isn't an input byte, just return 0 */  
      cli();                                                  /* turn off interrupts, for safety */  
      c = rxBuf[rxHed];                                       /* fetch the byte from the head of the buffer */  
      rxHed = (rxHed + 1) < BUFFSIZE ? rxHed + 1 : 0;         /* adjust the pointer */  
      --rxSiz;                                                /* decrement the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     }  
     return c;  
    }  
    /*==============================*/  
    ISR(USART_RX_vect) {                                      /* interestingly, the interrupt looks just like the runtime */  
     UByte c = UDR0;                                          /* fetch the byte from the input port */  
     if (rxSiz < BUFFSIZE) {                                  /* make sure there is room in the buffer, but because it is the interrupt, can't wait */  
      cli();                                                  /* block the other interrupts, for safety */  
      rxBuf[rxTal] = c;                                       /* put the byte on the end of the buffer */  
      rxTal = (rxTal + 1) < BUFFSIZE ? rxTal + 1 : 0;         /* adjust the pointer */  
      rxSiz++;                                                /* increment the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     } else { /* Dropped bytes go here */ }  
    }  
    ISR(USART_TX_vect) {                                      /* this could nearly be a cut and paste, but the buffer names are swapped */  
     UByte c;  
     if (txSiz > 0) {                                         /* special case for last one out */  
      cli();                                                  /* block the other interrupts, for safety */  
      c = txBuf[txHed];                                       /* fetch the byte from the head of the buffer */  
      txHed = (txHed + 1) < BUFFSIZE ? txHed + 1 : 0;         /* adjust the pointer */  
      --txSiz; UDR0 = c;                                      /* decrement the character counter, and put the byte in the output port */  
      sei();                                                  /* all done so turn the interrupts back on */  
     } else {  
      UCSR0B &= ~TXI0;                                        /* last byte out, disable the interrupt */
     } 
    } 
    /* end of file */

     

    EEPROM.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    UByte getEEPROM(UWord ofs) {  
     UByte rVal = 255;  
     if (ofs <= E2END) {  
      while(EECR & (1 << EEPE)); EEAR = ofs;  
      EECR |= (1 << EERE); rVal = EEDR;  
     }  
     return rVal;  
    }  
    void putEEPROM(UWord ofs, UByte d) {  
     if (ofs <= E2END) {  
      while(EECR & (1 << EEPE)); EEAR = ofs;  
      EEDR = d; EECR |= (1 << EEMPE); EECR |= (1 << EEPE);  
     }  
    }  
    /* end of file */

     

    and the file that bridges the gap TIMERS.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    #define TIMSK0_INIT  (1<<OCIE0A)  
    #define TIMSK2_INIT  0  
    #define TIMSK1_INIT  (1<<TOIE1)  
    #define TCCR0A_INIT  0  
    #define TCCR0B_INIT  (3<<CS00)  
    #define TCCR2A_INIT  0  
    #define TCCR2B_INIT  (3<<CS00)  
    #define TCCR1A_INIT  0  
    #define TCCR1B_INIT  (2<<CS10)  
    #define TCCR1C_INIT  0  
      
    #define mS_UPDATE_8  250  
    void setTach(bool);
      
    SLong ov0Tic;  
    SLong ov2Tic;  
    SLong ov1Tic;  
    SLong getTime1(void) { ULong rVal; cli(); rVal = (ov1Tic | TCNT1); sei(); return rVal; }  
      
    void initTimers(void) {  
     TCCR0A = TCCR0A_INIT; TCCR0B = TCCR0B_INIT; TIMSK0 = TIMSK0_INIT;  
     TCCR2A = TCCR2A_INIT; TCCR2B = TCCR2B_INIT; TIMSK2 = TIMSK2_INIT;  
     TCCR1A = TCCR1A_INIT; TCCR1B = TCCR1B_INIT; TCCR1C = TCCR1C_INIT; TIMSK1 = TIMSK1_INIT;  
    }  
    ISR(TIMER0_COMPA_vect) {  
      OCR0A += mS_UPDATE_8;  /* refresh for 1mS Timer Tic */  
      if (dlyTkHi > 0) { --dlyTkHi; if (dlyTkHi == 0) { OCR1A = etTkHi; TIMSK1 |= (1 << OCIE1A); } }  
      if (dlyTkLo > 0) { --dlyTkLo; if (dlyTkLo == 0) { OCR1B = etTkLo; TIMSK1 |= (1 << OCIE1B); } }  
    }  
    ISR(TIMER0_OVF_vect) { ov0Tic = (ov0Tic +   256L) & 0x07ffff00; } 
     
    ISR(TIMER2_COMPA_vect) { TIMSK2 &= ~(1 << OCIE2A); setTach(true); }  
    ISR(TIMER2_COMPB_vect) { TIMSK2 &= ~(1 << OCIE2B); setTach(false); }  
    ISR(TIMER2_OVF_vect) {  
        ov2Tic = (ov2Tic +   256L) & 0x07ffff00;  
        if ((ov2Tic == dlyTkHi) && (tkHi == 1)) { /* timer match and flag on */  
          tkHi = 0;                               /* condition met, so turn off flag */  
          if (etTkHi > 0) {                       /* special condition if low order portion is 0 */  
            OCR2A = etTkHi; TIMSK2 |= (1 << OCIE2A); /* everything regular, so just like the 16 bit */  
          } else {                                /* if short portion is 0 the event is now */  
            setTach(true);                        /* tach on */  
          }  
        }  
        if ((ov2Tic == dlyTkLo) && (tkLo == 1)) { /* timer match and flag on */  
          tkLo = 0;                               /* condition met, so turn off flag */  
          if (etTkLo > 0) {                       /* everything else the same */  
            OCR2B = etTkHi; TIMSK2 |= (1 << OCIE2B);  
          } else {  
            setTach(false);                      /* tach on */  
          }  
        }  
    }   
    ISR(TIMER1_OVF_vect) { ov1Tic = (ov1Tic + 65536L) & 0x3fff0000; }  
    /************************************************************************************************** 
     Timer 1 is a 16 bit timer running fast 
     Timer 1 group is used for coil action A on B off 
     Activity for both is to perform the operation and disable the interrupt 
    **************************************************************************************************/  
    ISR(TIMER1_COMPA_vect) { TIMSK1 &= ~(1 << OCIE1A); setTach(true);  }  
    ISR(TIMER1_COMPB_vect) { TIMSK1 &= ~(1 << OCIE1B); setTach(false); }  
    /* end of file */ 

     

    The tach signal is used twice in the timer, because we haven't covered ignition or fueling only event timing. So the next file is the SCHEDULER.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    UWord etTkHi, etTkLo;  
    SWord dlyTkHi, dlyTkLo;  
      
    /************************************************************************************************** 
    Generated TACH signal 
    **************************************************************************************************/  
    void ignTkHi(SLong et, SLong bt) {  
        SLong ev = (et + bt + 4) >> 3;  
      
        etTkHi = (UByte)(ev & 0xff);  
        dlyTkHi = ev & 0x07ffff00;  
        tkHi = 1;  
    }  
    void ignTkLo(SLong et, SLong bt) {  
        SLong ev = (et + bt + 4) >> 3;  
      
        etTkLo = (UByte)(ev & 0xff);  
        dlyTkLo = ev & 0x07ffff00;  
        tkLo = 1;  
    }  
    /************************************************************************************************** 
    Generated TACH signal first version high speed timer
    **************************************************************************************************/  
    //void ignTkHi(SLong et, SLong bt) {  
    // SLong ev = et + bt;               /* get the timer event compare value */  
    //  
    // etTkHi = (UWord)(ev & 0xffff);    /* the value is masked to 16 bits to match the compare register */  
    // if (et > THREE_mS) {              /* check for long delay and set 1ms timeout */  
    //  dlyTkHi = (SWord)((et - TWO_mS) / TICS_PER_mS);  
    // } else {                          /* no delay */  
    //  OCR1A = etTkHi;                  /* set the timer compare register */  
    //  TIMSK1 |= (1 << OCIE1A);         /* and enable the interrupt */  
    // }  
    //}  
    //void ignTkLo(SLong et, SLong bt) {  
    // SLong ev = et + bt;               /* get the timer event compare value */  
    //  
    // etTkLo = (UWord)(ev & 0xffff);    /* the value is masked to 16 bits to match the compare register */  
    // if (et > THREE_mS) {              /* check for long delay and set 1ms timeout */  
    //  dlyTkLo = (SWord)((et - TWO_mS) / TICS_PER_mS);  
    // } else {                          /* no delay */  
    //  OCR1B = etTkLo;                  /* set the timer compare register */  
    //  TIMSK1 |= (1 << OCIE1B);         /* and enable the interrupt */  
    // }  
    //}  
    /************************************************************************************************** 
    Parameters: 
       min/max - start and end angle of window for sample 
       pa - angle of last event, last tooth 
       bs - magic BASE 
       tm - time of last event, last tooth 
    **************************************************************************************************/  
    void schedule(UWord min, UWord max, UWord pa, SLong bs, SLong tm) {  
     SLong ft;  
     UByte c;  
      
     if (min < max) {  
      for (c = 0; c < cylCt; c++) {  
       if ((min < tkSt[c]) && (tkSt[c] <= max)) { ft = (SLong)(tkSt[c] - pa) * bs / PART2; ignTkHi(ft, tm); }  
       if ((min < tkEv[c]) && (tkEv[c] <= max)) { ft = (SLong)(tkEv[c] - pa) * bs / PART2; ignTkLo(ft, tm); }  
     } else {  
      for (c = 0; c < spEvnt; c++) {  
       if ((min < tkSt[c]) || (tkSt[c] <= max)) { ft = (SLong)(tkSt[c] - pa) * bs / PART2; ignTkHi(ft, tm); }  
       if ((min < tkEv[c]) || (tkEv[c] <= max)) { ft = (SLong)(tkEv[c] - pa) * bs / PART2; ignTkLo(ft, tm); }  
     }  
    }
    /* end of file */

     

    The latching file LATCHES.c handles the special hardware with the latching chips.

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    #define MAX_LATCH            32  
      
    static const UByte lchMask[] = {  
         0x01,0x02,0x04,0x08,0x11,0x12,0x14,0x18,0x21,0x22,0x24,0x28,0x31,0x32,0x34,0x38  
        ,0x41,0x42,0x44,0x48,0x51,0x52,0x54,0x58,0x61,0x62,0x64,0x68,0x71,0x72,0x74,0x78  
    };  
    static UByte lch[8];  
    void initLatches(void) {  
        UByte c;  
        DDRB &= 0xf0; DDRD &= 0x0f;  
        for (c = 0; c < 8; c++) { lch[c] = 0; PORTB = 0; PORTD = c; PORTD = (PIND | 0x80); }  
    }  
    void setLatch(UByte n, bool t) {  
        UByte a, b, c;  
      
        if (n < MAX_LATCH) {  
            b = lchMask[n] & 0x0f; a = lchMask[n] & 0x70; c = n / 4;  
            lch[c] = t ? (lch[c] | b) : (lch[c] & ~b); PORTB = lch[c];  
            PORTD = (PIND & 0x0f) | a; PORTD = (PIND | 0x80);  
        }  
    }  
    bool isLatchOn(UByte n) {  
        return (n < MAX_LATCH ? ((lch[n>>2] & (lchMask[n] & 0x0f)) != 0) : false);  
    }  
    /* end of file */

     

    and last, the input manager for cam and crank signals IGNCAMCRK.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
      
    #include "types.h"  
      
    #define EICRA_INIT   (1<<ISC00)|(1<<ISC10) /* Trigger INT0 and INT1 on either edge */  
    #define EIMSK_INIT   (1<<INT0)|(1<<INT1)   /* Set INT0 and INT1 active */  
    #define QUART_SEC     250                  /* provide a 250 mS timeout 1/4 second */
      
    /* Calibration value in future, but for now just provide constants */  
    SWord getCrankToothCt(void) { return 4; }  
    SWord getCylCount(void) { return 4; }
    SWord getCylAngle(UByte) { return (16384*c); }
    
    bool isCrkRising(void) { return false; } /* these are future calibration values, but for now force the false falling edge */  
    bool isCamRising(void) { return false; }  
    bool isInt0Low(void) { return ((PIND & (1<<PD2)) != 0); }  
    bool isInt1Low(void) { return ((PIND & (1<<PD3)) != 0); }  
      
    SLong crkTime;  
    SLong crkDiff;  
    SLong camTIme;  
    SLong camDiff;  
      
    SWord teeth;  
    SWord rpm;  
    SWord preRpm;  
    UWord camAngle;
    UByte camError;
    UWord toothAngle;
    UByte toothError;
    UByte tmOut;
      
    void initIgn(void) {  
     EICRA = EICRA_INIT;  
     EIMSK = EIMSK_INIT;  
     crkTime = camTime = 0;  
     toothAfterCam = 0;  
     tmOut = 0;
    }  
    void rtUpdateIgn(void) {  
     if (tmOut > 0) {  
      --tmOut;  
      rpm = preRpm / crkDiff;  
     } else {  
      teeth = getCrankToothCt();  
      preRpm = TICS_PER_MIN / teeth;
      toothAngle = DEG_PER_REV / teeth;  
      toothError = DEG_PER_REV % teeth;
      cylCt = (UByte)getCylCount();  
      for (c = 0; c < cylCt; c++) {  
        tkEv[c] = getCylAngle(c);  
        tkSt[c] = tkEv[c] - FIVE_DEG;  
      }  
     }  
    }  
    /************************************************************************************************** 
     Crank interrupt 
     Interrupt is called on both rising and falling edges of crank signal. 
     - Active edge is defined as falling edge, or rising edge 
        Active edge is determined as falling and signal low, or rising and signal high. 
     - Primary activity for the interrupt is to determine angular velocity and cam angle 
       Using high speed clock timer, obtain current time as tempTime 
       and using previous time (crkTime) calculate a difference between active signals. 
     - Preserve time and difference as crkTime and crkDiff 
    **************************************************************************************************/  
    ISR(INT0_vect) {  
     SLong tmpTime = getTime1();  
     SLong tmpDiff = tmpTime + (tmpTime > crkTime ? 0 : 0x40000000) - crkTime;  
    /************************************************************************************************** 
     Because the timer has a limit of a max value before rollover, the 32 bit number is limited 
     to only using 30 bits before rollover. At rollover occurrence a factor is added to retain 
     the proper value for DIFF. TIME is unaffected. 
    **************************************************************************************************/  
     if (isInt0Low() ^ isCrkRising()) {  
    /************************************************************************************************** 
      use the cam signal to locate the tooth 
    **************************************************************************************************/  
      tmOut = QUART_SEC;  
      camAngle += toothAngle;  
      camError += toothError; if (toothError >= teeth) { camAngle++; camError -= teeth; }  
      if (isCamDet()) {  
       setCamDet(false); /* cam detected so clear the flag */  
       camAngle = 0;  
       camError = 0;
      }  
      crkTime = tmpTime;  
      crkDiff = tmpDiff;  
    /* this doesn't provide correction for partial error correction */  
      min = camAngle + (toothAngle / 2);  
      max = min + toothAngle;  
    /* this is actually a good place to invoke the scheduler, as long as it isn't */  
    /* called to do too much in its operation. */  
     }  
    }  
    /************************************************************************************************** 
     Cam interrupt 
    **************************************************************************************************/  
    ISR(INT1_vect) {  
     SLong tmpTime = getTime1();  
     SLong tmpDiff = tmpTime + (tmpTime > crkTime ? 0 : 0x40000000) - camTime;  
     if (isInt1Low() ^ isCamRising()) {  
      setCamDet(true); /* simple signal for now */  
      camTime = tmpTime;  
      camDiff = tmpDiff;  
     }  
    }  
    /* end of file */ 

     

    That should catch things up for now, I think I got everything, Probably should have run a compile on all this stuff to make sure, but what fun would that be. Besides, if there is something wrong, then it gives an opportunity to get questions. Otherwise, I give my 100% guarantee. it either will work or it won't. I guess the setTach() call isn't attached to anything at the moment.

     

    Jack

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • jack.chaney56
    jack.chaney56 over 7 years ago

    Big one today as promised...

     

    First is types.h which hasn't changed particularly from the first. Just added some #define stuff at the end. There should be a device specific general .h file (usually the project name)

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
      
     This is the base include file, that pulls in all the AVR components, and standard items to make 
     life easier. I also declare some items for variable naming that makes it easier for me 
     to run on auto pilot.
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #ifndef _TYPES_H  
    #define _TYPES_H  
    #include <stdbool.h>  
    #include <stdio.h>  
    #include <avr/interrupt.h>  
    #include <avr/io.h>  
      
    typedef unsigned char  UByte;  
    typedef char           SByte;  
    typedef unsigned short UWord;  
    typedef short          SWord;  
    typedef unsigned long  ULong;  
    typedef long           SLong;  
      
    /* Hardware interfacers */  
    #define REG_B(a)      *((volatile UByte* const)(a))  
    #define REG_W(a)      *((volatile UWord* const)(a))  
    #define REG_L(a)      *((volatile ULong* const)(a))  
    #define REG_Bs(a)     *((volatile SByte* const)(a))  
    #define REG_Ws(a)     *((volatile SWord* const)(a))  
    #define REG_Ls(a)     *((volatile SLong* const)(a))  
    #define ARR_B(a)      ((UByte* const)(a))  
    #define ARR_W(a)      ((UWord* const)(a))  
    #define ARR_L(a)      ((ULong* const)(a))  
    #define ARR_Bs(a)     ((SByte* const)(a))  
    #define ARR_Ws(a)     ((SWord* const)(a))  
    #define ARR_Ls(a)     ((SLong* const)(a))  
      
    /* some use while(1) but I found this way compiles to fewer instructions */  
    #ifdef forever  
    #undef forever  
    #endif  
    #define forever    for(;;)  
      
    /* handy bits */  
    #define bit0    1  
    #define bit1    2  
    #define bit2    4  
    #define bit3    8  
    #define bit4    16  
    #define bit5    32  
    #define bit6    64  
    #define bit7    128  
    #define bit8    256  
    #define bit9    512  
    #define bit10    1024  
    #define bit11    2048  
    #define bit12    4096  
    #define bit13    8192  
    #define bit14    16384  
    #define bit15    32768  
      
    #define PART1    64L        /* part 1 of full circle for calculations */  
    #define PART2    512L       /* part 2 of full circle for calculations */  
    #define DEGS_PER_REV  (PART1*PART2)  
    #define TICS_PER_mS     2000  
    #define mS_PER_SEC      1000  
    #define SEC_PER_MIN     60  
    #define TICS_PER_MIN    (TICS_PER_mS*mS_PER_SEC*SEC_PER_MIN)  
    
    
    extern UWord etTkHi, etTkLo;  
    extern SWord dlyTkHi, dlyTkLo;  
    
    
    #endif  
    /* end of file */ 

     

    The second group are the files related to the hardware A2D.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    #define ADCSRA_INIT   (7<<ADPS0)     /* Right aligned Free running /64 prescaler */  
    #define A2D_CHAN   8                 /* number of A/D channels */  
    #define A2D_FILTER   8               /* 8 to 1 filter */  
    #define A2D_HALF   (A2D_FILTER/2)    /* used for rounding the result */  
      
    static UByte a2dSemi4;               /* pseudo semaphore, are updates being done */  
    static SWord a2dVal[A2D_CHAN];  
    static UByte a2dChan;                /* current converting A/D */  
      
    SWord getA2d(UByte v) { return (v < A2D_CHAN ? ((a2dVal[v] + A2D_HALF) / A2D_FILTER) : 0); }  
      
    void initA2D(void) {  
     a2dSemi4 = 0;                       /* pseudo semaphore, to only run the conversions when ready */  
     for (a2dChan = 0; a2dChan < A2D_CHAN; a2dChan++) {  
      a2dVal[a2dChan] = 0;               /* clear the buffer */  
     }  
     ADCSRA = ADCSRA_INIT;               /* configure the A/D */  
    }  
    void rtUpdateA2D(void) {  
     if (a2dSemi4 == 0) {                /* don't run if semaphore is active(already running) */  
      a2dSemi4 = 1;                      /* started, so set the semaphore on */  
      a2dChan = 0;                       /* start with the first A/D */  
      ADMUX = a2dChan;                   /* set the A/D channel */  
      ADCSRA |= (1 << ADEN);             /* enable the A/D channel */  
      ADCSRA |= (1 << ADSC);             /* start the A/D conversion */  
      ADCSRA |= (1 << ADIE);             /* turn on the interrupt */  
     }  
    }  
    ISR(ADC_vect) {  
     SWord tmp = ADC;                    /* conversion complete, so fetch the A/D value */  
     ADCSRA &= ~(1<<ADEN);               /* disable the A/D while changing the MUX */  
    /* the filtering is a running average... remove one part and add in the new as replacement */  
     a2dVal[a2dChan] += tmp - (a2dVal[a2dChan] / A2D_FILTER);  
     a2dChan++;                          /* point to the next channel */  
     if (a2dChan < A2D_CHAN) {           /* check to see if all the channels are done */  
      ADMUX = a2dChan;                   /* set the A/D channel */  
      ADCSRA |= (1 << ADEN);             /* enable the A/D channel */  
      ADCSRA |= (1 << ADSC);             /* start the A/D conversion */  
     } else {  
      ADCSRA &= ~(1 << ADIE);            /* all the channels read, so turn off the interrupt */  
      a2dSemi4 = 0;                      /* and clear the interrupt */  
     }  
    }  
    /* end of file */

     

    UART.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    #define UBAUD_57_6    34  
    #define UBAUD_38_4    51  
    #define UBAUD_19_2    103  
    #define UBAUD__9_6    207  
      
    #define BUFFSIZE      32  
      
    #define TXI0          (1<<TXCIE0)  
    #define UCSRA_INIT    (1<<U2X0)                           /* Timebase for UART 2x clock */  
    #define UCSRB_INIT    (1<<TXEN0)|(1<<RXEN0)|(1<<RXCIE0)   /* Enable transmit and receive lines and receive interrupt */  
    #define UCSRC_INIT    (3<<UCSZ00)|(0<<UPM00)|(0<<UMSEL00) /* n-8-1 */  
    #define COMMSPEED     UBAUD_19_2                          /* 19200 */  
      
    static UByte rxBuf[BUFFSIZE];  
    static UByte txBuf[BUFFSIZE];  
    static volatile UByte rxHed, rxTal, rxSiz;  
    static volatile UByte txHed, txTal, txSiz;  
      
    bool isComReady(void) { return (rxSiz != 0); }  
      
    void initUart(void) {  
     UCSR0A = UCSRA_INIT;  
     UCSR0B = UCSRB_INIT;  
     UCSR0C = UCSRC_INIT;  
     UBRR0 = COMMSPEED;  
     rxHed = 0; rxTal = 0; rxSiz = 0;  
     txHed = 0; txTal = 0; txSiz = 0;  
    }  
    /*==============================*/  
    /*       *** WARNING ***        */  
    /*  Do not use these functions  */  
    /*     within an interrupt      */  
    /*==============================*/  
    void putCom(UByte c) {  
     if ((UCSR0B & TXI0) == 0) {                              /* output buffer empty and no byte currently being sent */  
      UDR0 = c; UCSR0B |= TXI0;                               /* put byte in output port, and turn on the interrupt */  
     } else {  
      while (txSiz >= BUFFSIZE);                              /* make sure there is room in the buffer, or wait (see warning) */  
      cli();                                                  /* turn off interrupts, for safety */  
      txBuf[txTal] = c;                                       /* put the byte on the end of the buffer */  
      txTal = (txTal + 1) < BUFFSIZE ? txTal + 1 : 0;         /* adjust the pointer */  
      txSiz++;                                                /* increment the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     }  
    }  
    UByte getCom(void) {  
     UByte c = 0;  
     if (rxSiz > 0) {                                         /* if there isn't an input byte, just return 0 */  
      cli();                                                  /* turn off interrupts, for safety */  
      c = rxBuf[rxHed];                                       /* fetch the byte from the head of the buffer */  
      rxHed = (rxHed + 1) < BUFFSIZE ? rxHed + 1 : 0;         /* adjust the pointer */  
      --rxSiz;                                                /* decrement the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     }  
     return c;  
    }  
    /*==============================*/  
    ISR(USART_RX_vect) {                                      /* interestingly, the interrupt looks just like the runtime */  
     UByte c = UDR0;                                          /* fetch the byte from the input port */  
     if (rxSiz < BUFFSIZE) {                                  /* make sure there is room in the buffer, but because it is the interrupt, can't wait */  
      cli();                                                  /* block the other interrupts, for safety */  
      rxBuf[rxTal] = c;                                       /* put the byte on the end of the buffer */  
      rxTal = (rxTal + 1) < BUFFSIZE ? rxTal + 1 : 0;         /* adjust the pointer */  
      rxSiz++;                                                /* increment the character counter */  
      sei();                                                  /* all done so turn the interrupts back on */  
     } else { /* Dropped bytes go here */ }  
    }  
    ISR(USART_TX_vect) {                                      /* this could nearly be a cut and paste, but the buffer names are swapped */  
     UByte c;  
     if (txSiz > 0) {                                         /* special case for last one out */  
      cli();                                                  /* block the other interrupts, for safety */  
      c = txBuf[txHed];                                       /* fetch the byte from the head of the buffer */  
      txHed = (txHed + 1) < BUFFSIZE ? txHed + 1 : 0;         /* adjust the pointer */  
      --txSiz; UDR0 = c;                                      /* decrement the character counter, and put the byte in the output port */  
      sei();                                                  /* all done so turn the interrupts back on */  
     } else {  
      UCSR0B &= ~TXI0;                                        /* last byte out, disable the interrupt */
     } 
    } 
    /* end of file */

     

    EEPROM.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    UByte getEEPROM(UWord ofs) {  
     UByte rVal = 255;  
     if (ofs <= E2END) {  
      while(EECR & (1 << EEPE)); EEAR = ofs;  
      EECR |= (1 << EERE); rVal = EEDR;  
     }  
     return rVal;  
    }  
    void putEEPROM(UWord ofs, UByte d) {  
     if (ofs <= E2END) {  
      while(EECR & (1 << EEPE)); EEAR = ofs;  
      EEDR = d; EECR |= (1 << EEMPE); EECR |= (1 << EEPE);  
     }  
    }  
    /* end of file */

     

    and the file that bridges the gap TIMERS.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    #define TIMSK0_INIT  (1<<OCIE0A)  
    #define TIMSK2_INIT  0  
    #define TIMSK1_INIT  (1<<TOIE1)  
    #define TCCR0A_INIT  0  
    #define TCCR0B_INIT  (3<<CS00)  
    #define TCCR2A_INIT  0  
    #define TCCR2B_INIT  (3<<CS00)  
    #define TCCR1A_INIT  0  
    #define TCCR1B_INIT  (2<<CS10)  
    #define TCCR1C_INIT  0  
      
    #define mS_UPDATE_8  250  
    void setTach(bool);
      
    SLong ov0Tic;  
    SLong ov2Tic;  
    SLong ov1Tic;  
    SLong getTime1(void) { ULong rVal; cli(); rVal = (ov1Tic | TCNT1); sei(); return rVal; }  
      
    void initTimers(void) {  
     TCCR0A = TCCR0A_INIT; TCCR0B = TCCR0B_INIT; TIMSK0 = TIMSK0_INIT;  
     TCCR2A = TCCR2A_INIT; TCCR2B = TCCR2B_INIT; TIMSK2 = TIMSK2_INIT;  
     TCCR1A = TCCR1A_INIT; TCCR1B = TCCR1B_INIT; TCCR1C = TCCR1C_INIT; TIMSK1 = TIMSK1_INIT;  
    }  
    ISR(TIMER0_COMPA_vect) {  
      OCR0A += mS_UPDATE_8;  /* refresh for 1mS Timer Tic */  
      if (dlyTkHi > 0) { --dlyTkHi; if (dlyTkHi == 0) { OCR1A = etTkHi; TIMSK1 |= (1 << OCIE1A); } }  
      if (dlyTkLo > 0) { --dlyTkLo; if (dlyTkLo == 0) { OCR1B = etTkLo; TIMSK1 |= (1 << OCIE1B); } }  
    }  
    ISR(TIMER0_OVF_vect) { ov0Tic = (ov0Tic +   256L) & 0x07ffff00; } 
     
    ISR(TIMER2_COMPA_vect) { TIMSK2 &= ~(1 << OCIE2A); setTach(true); }  
    ISR(TIMER2_COMPB_vect) { TIMSK2 &= ~(1 << OCIE2B); setTach(false); }  
    ISR(TIMER2_OVF_vect) {  
        ov2Tic = (ov2Tic +   256L) & 0x07ffff00;  
        if ((ov2Tic == dlyTkHi) && (tkHi == 1)) { /* timer match and flag on */  
          tkHi = 0;                               /* condition met, so turn off flag */  
          if (etTkHi > 0) {                       /* special condition if low order portion is 0 */  
            OCR2A = etTkHi; TIMSK2 |= (1 << OCIE2A); /* everything regular, so just like the 16 bit */  
          } else {                                /* if short portion is 0 the event is now */  
            setTach(true);                        /* tach on */  
          }  
        }  
        if ((ov2Tic == dlyTkLo) && (tkLo == 1)) { /* timer match and flag on */  
          tkLo = 0;                               /* condition met, so turn off flag */  
          if (etTkLo > 0) {                       /* everything else the same */  
            OCR2B = etTkHi; TIMSK2 |= (1 << OCIE2B);  
          } else {  
            setTach(false);                      /* tach on */  
          }  
        }  
    }   
    ISR(TIMER1_OVF_vect) { ov1Tic = (ov1Tic + 65536L) & 0x3fff0000; }  
    /************************************************************************************************** 
     Timer 1 is a 16 bit timer running fast 
     Timer 1 group is used for coil action A on B off 
     Activity for both is to perform the operation and disable the interrupt 
    **************************************************************************************************/  
    ISR(TIMER1_COMPA_vect) { TIMSK1 &= ~(1 << OCIE1A); setTach(true);  }  
    ISR(TIMER1_COMPB_vect) { TIMSK1 &= ~(1 << OCIE1B); setTach(false); }  
    /* end of file */ 

     

    The tach signal is used twice in the timer, because we haven't covered ignition or fueling only event timing. So the next file is the SCHEDULER.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
    UWord etTkHi, etTkLo;  
    SWord dlyTkHi, dlyTkLo;  
      
    /************************************************************************************************** 
    Generated TACH signal 
    **************************************************************************************************/  
    void ignTkHi(SLong et, SLong bt) {  
        SLong ev = (et + bt + 4) >> 3;  
      
        etTkHi = (UByte)(ev & 0xff);  
        dlyTkHi = ev & 0x07ffff00;  
        tkHi = 1;  
    }  
    void ignTkLo(SLong et, SLong bt) {  
        SLong ev = (et + bt + 4) >> 3;  
      
        etTkLo = (UByte)(ev & 0xff);  
        dlyTkLo = ev & 0x07ffff00;  
        tkLo = 1;  
    }  
    /************************************************************************************************** 
    Generated TACH signal first version high speed timer
    **************************************************************************************************/  
    //void ignTkHi(SLong et, SLong bt) {  
    // SLong ev = et + bt;               /* get the timer event compare value */  
    //  
    // etTkHi = (UWord)(ev & 0xffff);    /* the value is masked to 16 bits to match the compare register */  
    // if (et > THREE_mS) {              /* check for long delay and set 1ms timeout */  
    //  dlyTkHi = (SWord)((et - TWO_mS) / TICS_PER_mS);  
    // } else {                          /* no delay */  
    //  OCR1A = etTkHi;                  /* set the timer compare register */  
    //  TIMSK1 |= (1 << OCIE1A);         /* and enable the interrupt */  
    // }  
    //}  
    //void ignTkLo(SLong et, SLong bt) {  
    // SLong ev = et + bt;               /* get the timer event compare value */  
    //  
    // etTkLo = (UWord)(ev & 0xffff);    /* the value is masked to 16 bits to match the compare register */  
    // if (et > THREE_mS) {              /* check for long delay and set 1ms timeout */  
    //  dlyTkLo = (SWord)((et - TWO_mS) / TICS_PER_mS);  
    // } else {                          /* no delay */  
    //  OCR1B = etTkLo;                  /* set the timer compare register */  
    //  TIMSK1 |= (1 << OCIE1B);         /* and enable the interrupt */  
    // }  
    //}  
    /************************************************************************************************** 
    Parameters: 
       min/max - start and end angle of window for sample 
       pa - angle of last event, last tooth 
       bs - magic BASE 
       tm - time of last event, last tooth 
    **************************************************************************************************/  
    void schedule(UWord min, UWord max, UWord pa, SLong bs, SLong tm) {  
     SLong ft;  
     UByte c;  
      
     if (min < max) {  
      for (c = 0; c < cylCt; c++) {  
       if ((min < tkSt[c]) && (tkSt[c] <= max)) { ft = (SLong)(tkSt[c] - pa) * bs / PART2; ignTkHi(ft, tm); }  
       if ((min < tkEv[c]) && (tkEv[c] <= max)) { ft = (SLong)(tkEv[c] - pa) * bs / PART2; ignTkLo(ft, tm); }  
     } else {  
      for (c = 0; c < spEvnt; c++) {  
       if ((min < tkSt[c]) || (tkSt[c] <= max)) { ft = (SLong)(tkSt[c] - pa) * bs / PART2; ignTkHi(ft, tm); }  
       if ((min < tkEv[c]) || (tkEv[c] <= max)) { ft = (SLong)(tkEv[c] - pa) * bs / PART2; ignTkLo(ft, tm); }  
     }  
    }
    /* end of file */

     

    The latching file LATCHES.c handles the special hardware with the latching chips.

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
    
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
    #include "types.h"  
      
    #define MAX_LATCH            32  
      
    static const UByte lchMask[] = {  
         0x01,0x02,0x04,0x08,0x11,0x12,0x14,0x18,0x21,0x22,0x24,0x28,0x31,0x32,0x34,0x38  
        ,0x41,0x42,0x44,0x48,0x51,0x52,0x54,0x58,0x61,0x62,0x64,0x68,0x71,0x72,0x74,0x78  
    };  
    static UByte lch[8];  
    void initLatches(void) {  
        UByte c;  
        DDRB &= 0xf0; DDRD &= 0x0f;  
        for (c = 0; c < 8; c++) { lch[c] = 0; PORTB = 0; PORTD = c; PORTD = (PIND | 0x80); }  
    }  
    void setLatch(UByte n, bool t) {  
        UByte a, b, c;  
      
        if (n < MAX_LATCH) {  
            b = lchMask[n] & 0x0f; a = lchMask[n] & 0x70; c = n / 4;  
            lch[c] = t ? (lch[c] | b) : (lch[c] & ~b); PORTB = lch[c];  
            PORTD = (PIND & 0x0f) | a; PORTD = (PIND | 0x80);  
        }  
    }  
    bool isLatchOn(UByte n) {  
        return (n < MAX_LATCH ? ((lch[n>>2] & (lchMask[n] & 0x0f)) != 0) : false);  
    }  
    /* end of file */

     

    and last, the input manager for cam and crank signals IGNCAMCRK.c

    /*************************************************************************************************** 
     All code provided is original and developed by Jack Chaney. Any similarity to code existing in 
     another location or form is purely coincidental. The code presented caries with it no guarantee 
     outside my statements that IT WORKED FOR ME. 
      
     If, in the future this code is used in any products, please provide proper recognition for my 
     efforts. 
      
     Jack Chaney, 2018 Chaney Firmware 
     
     History: [JAC] Original creation when the earth was cooling 
    ***************************************************************************************************/  
      
    #include "types.h"  
      
    #define EICRA_INIT   (1<<ISC00)|(1<<ISC10) /* Trigger INT0 and INT1 on either edge */  
    #define EIMSK_INIT   (1<<INT0)|(1<<INT1)   /* Set INT0 and INT1 active */  
    #define QUART_SEC     250                  /* provide a 250 mS timeout 1/4 second */
      
    /* Calibration value in future, but for now just provide constants */  
    SWord getCrankToothCt(void) { return 4; }  
    SWord getCylCount(void) { return 4; }
    SWord getCylAngle(UByte) { return (16384*c); }
    
    bool isCrkRising(void) { return false; } /* these are future calibration values, but for now force the false falling edge */  
    bool isCamRising(void) { return false; }  
    bool isInt0Low(void) { return ((PIND & (1<<PD2)) != 0); }  
    bool isInt1Low(void) { return ((PIND & (1<<PD3)) != 0); }  
      
    SLong crkTime;  
    SLong crkDiff;  
    SLong camTIme;  
    SLong camDiff;  
      
    SWord teeth;  
    SWord rpm;  
    SWord preRpm;  
    UWord camAngle;
    UByte camError;
    UWord toothAngle;
    UByte toothError;
    UByte tmOut;
      
    void initIgn(void) {  
     EICRA = EICRA_INIT;  
     EIMSK = EIMSK_INIT;  
     crkTime = camTime = 0;  
     toothAfterCam = 0;  
     tmOut = 0;
    }  
    void rtUpdateIgn(void) {  
     if (tmOut > 0) {  
      --tmOut;  
      rpm = preRpm / crkDiff;  
     } else {  
      teeth = getCrankToothCt();  
      preRpm = TICS_PER_MIN / teeth;
      toothAngle = DEG_PER_REV / teeth;  
      toothError = DEG_PER_REV % teeth;
      cylCt = (UByte)getCylCount();  
      for (c = 0; c < cylCt; c++) {  
        tkEv[c] = getCylAngle(c);  
        tkSt[c] = tkEv[c] - FIVE_DEG;  
      }  
     }  
    }  
    /************************************************************************************************** 
     Crank interrupt 
     Interrupt is called on both rising and falling edges of crank signal. 
     - Active edge is defined as falling edge, or rising edge 
        Active edge is determined as falling and signal low, or rising and signal high. 
     - Primary activity for the interrupt is to determine angular velocity and cam angle 
       Using high speed clock timer, obtain current time as tempTime 
       and using previous time (crkTime) calculate a difference between active signals. 
     - Preserve time and difference as crkTime and crkDiff 
    **************************************************************************************************/  
    ISR(INT0_vect) {  
     SLong tmpTime = getTime1();  
     SLong tmpDiff = tmpTime + (tmpTime > crkTime ? 0 : 0x40000000) - crkTime;  
    /************************************************************************************************** 
     Because the timer has a limit of a max value before rollover, the 32 bit number is limited 
     to only using 30 bits before rollover. At rollover occurrence a factor is added to retain 
     the proper value for DIFF. TIME is unaffected. 
    **************************************************************************************************/  
     if (isInt0Low() ^ isCrkRising()) {  
    /************************************************************************************************** 
      use the cam signal to locate the tooth 
    **************************************************************************************************/  
      tmOut = QUART_SEC;  
      camAngle += toothAngle;  
      camError += toothError; if (toothError >= teeth) { camAngle++; camError -= teeth; }  
      if (isCamDet()) {  
       setCamDet(false); /* cam detected so clear the flag */  
       camAngle = 0;  
       camError = 0;
      }  
      crkTime = tmpTime;  
      crkDiff = tmpDiff;  
    /* this doesn't provide correction for partial error correction */  
      min = camAngle + (toothAngle / 2);  
      max = min + toothAngle;  
    /* this is actually a good place to invoke the scheduler, as long as it isn't */  
    /* called to do too much in its operation. */  
     }  
    }  
    /************************************************************************************************** 
     Cam interrupt 
    **************************************************************************************************/  
    ISR(INT1_vect) {  
     SLong tmpTime = getTime1();  
     SLong tmpDiff = tmpTime + (tmpTime > crkTime ? 0 : 0x40000000) - camTime;  
     if (isInt1Low() ^ isCamRising()) {  
      setCamDet(true); /* simple signal for now */  
      camTime = tmpTime;  
      camDiff = tmpDiff;  
     }  
    }  
    /* end of file */ 

     

    That should catch things up for now, I think I got everything, Probably should have run a compile on all this stuff to make sure, but what fun would that be. Besides, if there is something wrong, then it gives an opportunity to get questions. Otherwise, I give my 100% guarantee. it either will work or it won't. I guess the setTach() call isn't attached to anything at the moment.

     

    Jack

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube