/*******************************************************************************
*
*  FREESCALE SEMICONDUCTOR INC. 2012-2014
*  ALL RIGHTS RESERVED
*  COPYRIGHT (c)
*
********************************************************************************
*
*  FILE NAME:  etpuc_crank.c
*
*  DESCRIPTION:
*    This eTPU function processes the tooth signal from a crankshaft sensor and 
*    generates an internal angle-base using the Enhanced Angle Counter
*    (EAC) eTPU hardware. The Angle Base is maintained in TCR2.
*    The Crank eTPU function keeps track of the overall synchronization state.
*    It reports via interrupt to the CPU when the overall synchronization state
*    changes. Additionally, and interrupt and DMA request (eTPU2 only) is 
*    generated periodically every engine cycle.
*
*    There are differences in functionality between this function and
*    the original set2 Crank. In order to achieve the full synchronization state,
*    this function asks the CPU to decode a Cam pattern logged during a defined
*    number of Crank teeth (teeth_per_sync) and set the angle value of the last
*    gap within 0-720. Thanks to this, the Crank (and Cam) function is much more
*    general and enables to handle a wide range of Crank patters:
*    - a single gap pattern or a multiple equally-spaced gaps
*    - a 1, 2 or 3-teeth gap (eTPU and eTPU2) or up to 7-teeth gap (eTPU2+)
*    - an additional tooth instead of the gap
*    Generally any kind of a Cam pattern, or even a pattern of more Cam signals,
*    can be handled, because the Cam log is decoded by the CPU.
*
*******************************************************************************/

/*******************************************************************************
*  Includes
*******************************************************************************/
#include "etpudef.h"       /* Defines eTPU hardware */
#include "etpuc_crank.h"
#include "etpuc_set.h"     /* Link4, GlobalError */

/*******************************************************************************
*  eTPU Function Parameters:
*
*   teeth_till_gap         - number of physical teeth gap to gap
*   teeth_in_gap           - number if missing teeth in the gap.
*                            If there is an additional tooth instead of the gap,
*                            this parameter must be set to 0.
*   teeth_per_cycle        - number of teeth (including missing teeth in gap)
*                            per an engine cycle (720 degrees). It must be
*                            a multiple of (teeth_till_gap + teeth_in_gap).
*   teeth_per_sync         - number of teeth (including missing teeth in gap)
*                            corresponding to a segment needed for Cam log.
*                            It must be a multiple of
*                            (teeth_till_gap + teeth_in_gap).
*   tooth_counter_gap      - it counts from 1 to teeth_till_gap
*   tooth_counter_cycle    - it counts from 1 to teeth_per_cycle
*   blank_teeth            - number of teeth ignored after initialization
*   blank_time             - TCR1 time period after initialization during which
*                            teeth are ignored,
*   tcr2_ticks_per_tooth   - number of TCR2 angle ticks per tooth
*   tcr2_ticks_per_add_tooth-number of TCR2 angle ticks from the last tooth to the additional tooth
*   last_tooth_tcr1_time   - TCR1 time of the last tooth transition
*   last_tooth_period      - TCR1 period between last 2 teeth
*   last_tooth_period_norm - TCR1 period normalized over the gap or additional tooth
*   tcr2_adjustment        - TCR2 angle value corresponding to the angle on the
*                            first tooth after gap, at which the PRE_FULL_SYNC
*                            state was set and CPU was asked to recognize the
*                            Cam log pattern.
*   gap_ratio              - fraction used to perform the ABA gap test:
*                              gap_ratio * tooth_period_B > tooth_period_A
*   win_ratio_normal       - fraction used to derive the acceptance window for
*                            the next normal tooth
*   win_ratio_across_gap   - fraction used to derive the acceptance window for
*                            the first tooth after the gap
*   win_ratio_after_gap    - fraction used to derive the acceptance window for
*                            the second tooth after the gap
*   win_ratio_after_timeout- fraction used to derive the acceptance window for
*                            the tooth following a timeout condition
*   first_tooth_timeout    - TCR1 time after the first tooth (after blank_teeth)
*                            when a timeout will be deemed to have happened
*   link_cam               - set of 4 link numbers to send to reset the Cam log
*                            (up to 4 Cam channel numbers)
*   link_1                 - the first  set of 4 link numbers to send on stall
*   link_2                 - the second set of 4 link numbers to send on stall
*   link_3                 - the third  set of 4 link numbers to send on stall
*   link_4                 - the fourth set of 4 link numbers to send on stall
*   state                  - used to keep track of the CRANK state. See header
*                            file for possible values.
*   error                  - crank error flags. See header file for individual
*                            bits meaning. The eTPU sets them, the CPU should
*                            read and clear.
*   *tooth_period_log      - pointer to an array of tooth periods.
*                            The array must include teeth_per_cycle items.
*   err2477_tcr2_target    - used to keep track of when the Angle Counter is not
*                            in high rate mode for errata 2477 workaround
*
********************************************************************************
*
*  Channel Flag usage
*    Flag0 is used to handle a crank wheel with gap(s) and a crank wheel with
*      additional tooth separately:
*      - CRANK_FLAG0_GAP
*      - CRANK_FLAG0_ADDITIONAL_TOOTH
*    Flag1 is not used.
*
********************************************************************************
*
*  Channel Function Mode (FM) bits usage
*    FM0 is used to select the polarity of crank transition to detect:
*      - CRANK_FM0_USE_TRANS_RISING
*      - CRANK_FM0_USE_TRANS_FALLING
*    FM1 is used to turn on logging of crank tooth periods:
*      - CRANK_FM1_LOG_TOOTH_PERIODS
*
********************************************************************************
*
*  Channel Interrupt usage
*    The channel interrupt on the CRANK channel is set when the global Engine
*    Position State is changed and on the first tooth every engine cycle when 
*    the global Engine Position State is FULL_SYNC. 
*    The Crank channel interrupt handler should follow this example:
*
*    eTPUCrankInterruptHandler()
*    {
*      * Read Engine Position State eng_pos_state.
*      switch(eng_pos_state)
*      {
*      case FS_ETPU_ENG_POS_SEEK:
*        * Crank has stalled. Read Crank error to know the reason.
*        break;
*      case FS_ETPU_ENG_POS_FIRST_HALF_SYNC:
*        * Crank has found the gap, or
*        * Crank did not received CRANK_HSR_SET_SYNC and hence Crank reset
*        * the Cam log to repeat recording the Cam log.
*        break;
*      case FS_ETPU_ENG_POS_PRE_FULL_SYNC:
*        * Cam signal is logged, the log corresponds to a segment of
*        * teeth_per_sync Crank teeth starting from a gap.
*        * NOW THE CPU MUST RECOGNIZE THE LOGGED CAM PATTERN AND
*        * 1) SET tcr2_adjustment
*        * 2) ASSIGN CRANK HSR = CRANK_HSR_SET_SYNC.
*        break;
*      case FS_ETPU_ENG_POS_FULL_SYNC:
*        * Regular interrupt on the first tooth every engine cycle.
*        break;
*      }
*    }
*
*******************************************************************************/
/*******************************************************************************
*  Global Variables
*******************************************************************************/
      uint8_t   eng_pos_state;
const uint24_t  eng_cycle_tcr2_ticks;
      uint24_t  eng_cycle_tcr2_start;

/*******************************************************************************
*  Local Functions
*******************************************************************************/
/*******************************************************************************
*  FUNCTION NAME: CRANK_Window_NoReturn
*  DESCRIPTION: Schedule transition acceptance window using win_ratio and
*    tooth_period, and end the thread.
*******************************************************************************/
void __attribute__((noreturn)) CRANK_Window_NoReturn(
	register_a fract24_t win_ratio,
	register_d uint24_t tooth_period)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;
	uint24_t half_window_width;

	half_window_width = f24i24mulu(win_ratio, tooth_period);
	erta = erta + tooth_period - half_window_width;
	ertb = erta + (half_window_width << 1);
	channel.MRLA = MRL_CLEAR;
	channel.MRLB = MRL_CLEAR;
	channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
	channel.ERWB = ERW_WRITE_ERT_TO_MATCH;
	channel.TDL = TDL_CLEAR;  /* clear noise transition, if any */
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_WindowAcrossGap_NoReturn
*  DESCRIPTION: Open transition acceptance window using win_ratio and
*    close it using win_ratio_across_gap and the number of teeth_in_gap,
*    and end the thread.
*******************************************************************************/
void __attribute__((noreturn)) CRANK_WindowAcrossGap_NoReturn(
	register_a uint24_t tooth_period)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;
	uint24_t half_window_width;

	half_window_width = f24i24mulu(crank_local.win_ratio_across_gap,
	                               tooth_period);
	ertb = erta + (tooth_period*(crank_local.teeth_in_gap + 1U))
			   + half_window_width;
	half_window_width = f24i24mulu(crank_local.win_ratio_normal,
	                               tooth_period);
	erta = erta + tooth_period - half_window_width;
	channel.MRLA = MRL_CLEAR;
	channel.MRLB = MRL_CLEAR;
	channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
	channel.ERWB = ERW_WRITE_ERT_TO_MATCH;
	channel.TDL = TDL_CLEAR;  /* clear noise transition, if any */
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_WindowCloseAt_NoReturn
*  DESCRIPTION: Open transition acceptance window immediately and
*    close it at close_tcr1_time, and end the thread.
*******************************************************************************/
void __attribute__((noreturn)) CRANK_WindowCloseAt_NoReturn(
	register_a uint24_t close_tcr1_time)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;

	erta = tcr1;
	ertb = close_tcr1_time;
	channel.MRLA = MRL_CLEAR;
	channel.MRLB = MRL_CLEAR;
	channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
	channel.ERWB = ERW_WRITE_ERT_TO_MATCH;
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_WindowClose_NoReturn
*  DESCRIPTION: Open transition acceptance window immediately and close using
*    win_ratio and tooth_period, and end the thread.
*******************************************************************************/
void __attribute__((noreturn)) CRANK_WindowClose_NoReturn(
	register_a fract24_t win_ratio,
	register_d uint24_t tooth_period)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;
	uint24_t half_window_width;

	half_window_width = f24i24mulu(win_ratio, tooth_period);
	ertb = erta + tooth_period + half_window_width;
	erta = tcr1;
	channel.MRLA = MRL_CLEAR;
	channel.MRLB = MRL_CLEAR;
	channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
	channel.ERWB = ERW_WRITE_ERT_TO_MATCH;
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_Stall_NoReturn
*  DESCRIPTION: Revert to FIRST_TRANS, signal other functions that Crank has
*    stalled.
*******************************************************************************/
void __attribute__((noreturn)) CRANK_Stall_NoReturn(void)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;

	/* set error */
	crank_local.error |= CRANK_ERR_STALL;
	/* set state */
	crank_local.state = CRANK_FIRST_TRANS;
	/* set global eng_pos state and channel interrupt */
	eng_pos_state = ENG_POS_SEEK;
	/* Channel interrupt */
	channel.CIRC = CIRC_INT_FROM_SERVICED;
	/* signal other functions that crank restarts */
	Link4(crank_local.link_1);
	Link4(crank_local.link_2);
	Link4(crank_local.link_3);
	Link4(crank_local.link_4);
	/* set default values */
	trr = 0xffffff;
	tpr = 0;
	/* reset TCR2 if it is positive, if it is negative it will be reset 
	   in CRANK_SEEK (prevent from TCR2 jump ahead) */
	if(tcr2 < 0x800000)
	{
		tcr2 = 0;
	}
	eng_cycle_tcr2_start = eng_cycle_tcr2_ticks;
	crank_local.tooth_counter_gap = 0;
	crank_local.tooth_counter_cycle = 0;
	crank_local.last_tooth_tcr1_time = 0;
	crank_local.last_tooth_period = 0;
	crank_local.last_tooth_period_norm = 0;
	/* open the acceptance window immediately and do not close it */
	erta = tcr1;
	channel.MRLA = MRL_CLEAR;
	channel.MRLB = MRL_CLEAR;
	channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_ToothArray_Log
*  DESCRIPTION: If enabled (FM1 set) log tooth_period 
*    into the tooth_period_log array at position [tooth_counter_cycle-1].
*******************************************************************************/
void CRANK_ToothArray_Log(
	register_a uint24_t tooth_period)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;
	uint24_t *ptr;

	if(cc.FM1 == CRANK_FM1_LOG_TOOTH_PERIODS)
	{
		ptr = crank_local.tooth_period_log + (crank_local.tooth_counter_cycle - 1);
		*ptr = tooth_period;
	}
}

/*******************************************************************************
*  FUNCTION NAME: CRANK_Set_TRR
*  DESCRIPTION: Calculates the tick rate and sets the Tick Rate Register (TRR).
*******************************************************************************/
void CRANK_Set_TRR(
	register_a uint24_t tooth_period)
{
	/* enable access to local channel parameters */
	volatile static register_chan_base struct CRANK_CHAN_PARAMS crank_local;
	extern register_mach  mach; /* MAC High register (keeps reminder after divition */

	trr = ((tooth_period/crank_local.tcr2_ticks_per_tooth) << 9)  /* integer part of TRR */
	       + (mach << 9)/crank_local.tcr2_ticks_per_tooth;        /* fractional part of TRR */				
}


/*******************************************************************************
*  eTPU Function Declaration
*******************************************************************************/
/* [MISRA 2004 Rule 3.4] usage of #pragma eTPU_function documented in 
   eTPU_Build_Tools_Reference.pdf, v.09/2013, chapter 26.4, p.304 */
#pragma ETPU_function CRANK, alternate;

void CRANK(
	struct CRANK_CHAN_PARAMS crank_local
);

/*******************************************************************************
*  eTPU Function
*******************************************************************************/
void CRANK(
	struct CRANK_CHAN_PARAMS crank_local
)
{
	volatile struct tpr_struct tpr_str @ register_tpr;
	uint24_t   tooth_period;
	uint24_t   half_window_width;

	/**************************************************************************
	* THREAD NAME: INIT
	* DESCRIPTION: Initialize the channel to run the CRANK function.
	**************************************************************************/
	if(hsr == CRANK_HSR_INIT)
	{
		/* disable matches in thread */
		match_disable();

		/* Stop the channel */
		/* Disable event handling */
		channel.MTD = MTD_DISABLE;
		/* Disable match detection */
		channel.MRLE = MRLE_DISABLE;
		/* Reset all latches */
		channel.TDL = TDL_CLEAR;
		channel.LSR = LSR_CLEAR;
		channel.MRLA = MRL_CLEAR;
		channel.MRLB = MRL_CLEAR;

		/* Initialize the channel */
		/* Set channel mode: mach2 ordered single transition */
		channel.PDCM = PDCM_M2_O_ST;
		/* Time base selection */
		channel.TBSA = TBS_M1C1GE;  /* capture time to erta*/
		channel.TBSB = TBS_M1C2GE;  /* capture angle to ertb */
		/* Input pin action control */
		channel.IPACA = IPAC_FALLING;
		channel.IPACB = IPAC_NO_DETECT;
		if(cc.FM0 == CRANK_FM0_USE_TRANS_RISING)
		{
			channel.IPACA = IPAC_RISING;
		}
		/* Output pin action control */
		channel.OPACA = OPAC_NO_CHANGE;
		channel.OPACB = OPAC_NO_CHANGE;

		/* Channel flags */
		channel.FLAG0 = CRANK_FLAG0_GAP;
		if(crank_local.teeth_in_gap == 0)
		{
			channel.FLAG0 = CRANK_FLAG0_ADDITIONAL_TOOTH;
		}

		/* Default values */
		trr = 0xffffff;
		tpr = 0;
		if((int24_t)tcr2 > 0)	tcr2 = 0;
		eng_pos_state = ENG_POS_SEEK;
		eng_cycle_tcr2_start = eng_cycle_tcr2_ticks;
		crank_local.state = CRANK_SEEK;

		/* Schedule Match A to open window */
		erta = tcr1 + 1; /* the +1 means that the window won't open until the
		                    timebase has been started */
		channel.ERWA = ERW_WRITE_ERT_TO_MATCH;

		/* Enable event handling */
		channel.MTD = MTD_ENABLE;
	}
	/**************************************************************************
	* THREAD NAME: ANGLE_ADJUST
	* DESCRIPTION: Update TCR2 value, set ENG_POS_FULL_SYNC.
	**************************************************************************/
	else if(hsr == CRANK_HSR_SET_SYNC)
	{
		/* disable matches in thread */
		match_disable();

		tcr2 += crank_local.tcr2_adjustment;
#ifdef ERRATA_2477
		crank_local.err2477_tcr2_target += crank_local.tcr2_adjustment;
#endif
		/* set global eng_pos state */
		eng_pos_state = ENG_POS_FULL_SYNC;
	}
	/**************************************************************************
	* THREAD NAME: CRANK_WITH_GAP
	* DESCRIPTION: A transition or a timeout, handling a crank wheel with gap.
	**************************************************************************/
	else if((m1 == 1) && (m2 == 1) && (flag0 == CRANK_FLAG0_GAP))
	{
		uint8_t  tmp;
		
		if(cc.TDLA == 1)
		{
			/* A tooth transition detected */
			channel.TDL = TDL_CLEAR;
			switch(crank_local.state)
			{
			case CRANK_SEEK:
				/**************************************************************
				* STATE: T0 - SEEK
				* DESCRIPTION: 
				*   First transition after INIT was detected.
				*   Wait for blank_time without detecting transitions.
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_BLANK_TIME;
				/* do not detect transitions */
				channel.IPACA = IPAC_NO_DETECT;
				/* keep window opened, close window after blank_time */
				CRANK_WindowCloseAt_NoReturn(erta + crank_local.blank_time);
				break;

			case CRANK_BLANK_TEETH:
				/**************************************************************
				* STATE: T2 - BLANK_TEETH
				* DESCRIPTION: 
				*   Count down blank_teeth without tooth period measurement.
				**************************************************************/
				tcr2 = 0;
				/* Count down blank_teeth */
				if(--crank_local.blank_teeth <= 0)
				{
					/* set_state */
					crank_local.state = CRANK_FIRST_TRANS;
				}
				break;

			case CRANK_FIRST_TRANS:
				/**************************************************************
				* STATE: T3 - FIRST_TRANS
				* DESCRIPTION: 
				*   First transition after blank_teeth was detected.
				*   Record transition time.
				*   Next transition is expected within first_tooth_timeout. 
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_SECOND_TRANS;
				/* record last_tooth_tcr1_time */
				crank_local.last_tooth_tcr1_time = erta;
				/* keep window opened, close window after first_tooth_timeout */
				CRANK_WindowCloseAt_NoReturn(erta + crank_local.first_tooth_timeout);
				break;

			case CRANK_SECOND_TRANS:
				/**************************************************************
				* STATE: T4 - SECOND_TRANS
				* DESCRIPTION: 
				*   Second transition after blank_teeth was detected.
				*   Calculate tooth period and record transition time.
				*   Next transition is expected within a long timeout over 
				*   a possible gap. 
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_TEST_POSSIBLE_GAP;
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				/* keep window opened, have to assume gap is next - close window after
				   a long timeout over a possible gap */
				half_window_width = f24i24mulu(crank_local.win_ratio_across_gap,
				                               tooth_period);
				CRANK_WindowCloseAt_NoReturn(erta +
					(tooth_period*(crank_local.teeth_in_gap + 1U)) + half_window_width);
				break;

			case CRANK_TEST_POSSIBLE_GAP:
				/**************************************************************
				* STATE: T5 - TEST_POSSIBLE_GAP
				* DESCRIPTION: 
				*   Transition detected, no synchronization yet.
				*   Calculate tooth period and record transition time.
				*   Test for a possible gap (AB of ABA test).
				*   If possible gap found, expect next transition in window 
				*     across gap.
				*   Else, expect next transition within a long timeout over 
				*     a possible gap.
				**************************************************************/
				tcr2 = 0;
				/* calc tooth_period and record last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				/* test for a possible gap (AB test) */
				if(f24i24mulu(crank_local.gap_ratio, tooth_period)
				   > crank_local.last_tooth_period)
				{ /* a possible gap found */
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					/* calculate an average tooth_period within the gap */
					crank_local.last_tooth_period_norm = tooth_period/(crank_local.teeth_in_gap + 1U);
					/* set state */
					crank_local.state = CRANK_VERIFY_GAP;
					/* open and close window using win_ratio_after_gap */
					CRANK_Window_NoReturn(crank_local.win_ratio_after_gap,
					                      crank_local.last_tooth_period_norm);
				}
				else
				{ /* gap not found */
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period;
					/* open and close window, have to assume the gap is next */
					CRANK_WindowAcrossGap_NoReturn(tooth_period);
				}
				break;

			case CRANK_VERIFY_GAP:
				/**************************************************************
				* STATE: T6 - VERIFY_GAP
				* DESCRIPTION: 
				*   Transition detected in window across gap, first sync.
				*   Calculate tooth period and record transition time.
				*   Verify a possible gap (BA of ABA test).
				*   If gap verified, this is the second tooth after gap,
				*     start TCR2, set ENG_POS_FIRST_HALF_SYNC and IRQ,
				*     reset Cam log, expect next transition in normal window. 
				*   Else, test possible gap again, expect next transition  
				*     within a long timeout over a possible gap.
				**************************************************************/
				/* calc tooth_period and record last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				/* verify a possible gap (BA portion of the ABA test) */
				if(f24i24mulu(crank_local.gap_ratio, crank_local.last_tooth_period)
				   > tooth_period)
				{ /* gap verified */
					/* set states */
					crank_local.state = CRANK_COUNTING;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period;
					/* set tooth counters - second tooth after gap */
					crank_local.tooth_counter_cycle = 2;
					//crank_local.tooth_counter_gap = 2;
					crank_local.tooth_counter_gap = 1;
					crank_local.tooth_counter_gap++;
					/* set TRR and TICKS */
					tpr_str.TICKS = (uint16_t)crank_local.tcr2_ticks_per_tooth - 1;
					CRANK_Set_TRR(tooth_period);
					tcr2 = crank_local.tcr2_ticks_per_tooth;
#ifdef ERRATA_2477
					crank_local.err2477_tcr2_target = crank_local.tcr2_ticks_per_tooth;
#endif
					/* set global eng_pos state and channel interrupt */
					eng_pos_state = ENG_POS_FIRST_HALF_SYNC;
					channel.CIRC = CIRC_INT_FROM_SERVICED;
					/* reset Cam log */
					Link4(crank_local.link_cam);
					/* open and close window using win_ratio_normal */
					CRANK_Window_NoReturn(crank_local.win_ratio_normal, tooth_period);
				}
				else
				{ /* gap not verified */
					/* set state */
					crank_local.state = CRANK_TEST_POSSIBLE_GAP;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period;
					/* open and close window, have to assume the gap is next */
					CRANK_WindowAcrossGap_NoReturn(tooth_period);
				}
				break;

			case CRANK_COUNTING_TIMEOUT:
				/**************************************************************
				* STATE: T8 - COUNTING_TIMEOUT
				* DESCRIPTION: 
				*   Transition detected within window after a single timeout.
				*   Recover and continue normal counting.
				**************************************************************/
				/* recover from counting timeout */
				/* set state */
				crank_local.state = CRANK_COUNTING;
				/* continue to normal processing at CRANK_COUNTING */

			case CRANK_COUNTING:
				/**************************************************************
				* STATE: T7 - COUNTING
				* DESCRIPTION: 
				*   Transition detected in normal window.
				*   Calculate tooth period and record transition time.
				*   Increment tooth counters.
				*   Check if the next tooth is the last before gap.
				*   Adjust TCR2 rate.
				*   Expect next transition in normal window. 
				**************************************************************/
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				crank_local.last_tooth_period_norm = tooth_period;
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* test if before the gap */
				if(crank_local.tooth_counter_gap == crank_local.teeth_till_gap - 1)
				{
					/* there is one more teeth till the gap */
					crank_local.state = CRANK_TOOTH_BEFORE_GAP;
				}
				/* set TRR */
				CRANK_Set_TRR(tooth_period);
#ifdef ERRATA_2477
				crank_local.err2477_tcr2_target += crank_local.tcr2_ticks_per_tooth;
#endif
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
				/* open and close window using win_ratio_normal */
				CRANK_Window_NoReturn(crank_local.win_ratio_normal, tooth_period);
				break;

			case CRANK_TOOTH_BEFORE_GAP:
				/**************************************************************
				* STATE: T9 - TOOTH_BEFORE_GAP
				* DESCRIPTION: 
				*   Transition detected in normal window, gap expected next.
				*   Calculate tooth period and record transition time.
				*   Increment tooth counters.
				*   Adjust TCR2 rate.
				*   Expect next transition within window across the gap. 
				**************************************************************/
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				crank_local.last_tooth_period_norm = tooth_period;
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* set TRR */
				CRANK_Set_TRR(tooth_period);
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
#ifdef ERRATA_2477
				/* set state */
				crank_local.state = CRANK_TOOTH_BEFORE_GAP_NOT_HRM;
				/* workaround for errata 2477
				 * a match is scheduled in angle so that the Angle Hardware is
				 * not in HRM */
				channel.TBSB = TBS_M2C2GE;
				/* open window immediately to enable match B
				   and schedule the match B for when the EAC will be out of HRM */
				CRANK_WindowCloseAt_NoReturn(crank_local.err2477_tcr2_target);
#else
				/* set state */
				crank_local.state = CRANK_TOOTH_AFTER_GAP;
				/* write MISSCNT */
				tpr |= crank_local.misscnt_mask;
				/* open and close window using win_ratio_normal
				   and win_ratio_across_gap */
				CRANK_WindowAcrossGap_NoReturn(tooth_period);
#endif
				break;

			case CRANK_TOOTH_AFTER_GAP:
				/**************************************************************
				* STATE: T11 - TOOTH_AFTER_GAP
				* DESCRIPTION: 
				*   Transition detected in window across gap.
				*   Calculate tooth period and record transition time.
				*   Verify the gap (AB of ABA test).
				*   If gap verified, adjust TCR2 rate and tooth counters,
				*     sync-cycle or engine-cycle is finished:
				*     In ENG_POS_FIRST_HALF_SYNC, 
				*       ask CPU to decode the Cam log, 
				*       reset TCR2, reset tooth_counter_cycle,
				*       set ENG_POS_PRE_FULL_SYNC and IRQ.
				*     In ENG_POS_PRE_FULL_SYNC, there was no response from CPU,
				*       reset Cam log, reset TCR2, reset tooth_counter_cycle,
				*       set ENG_POS_FIRST_HALF_SYNC and IRQ.
				*     In ENG_POS_FULL_SYNC,
				*       reset Cam log, reset tooth_counter_cycle, 
				*       set IRQ (once per cycle in full sync)
				*       increment eng_cycle_tcr2_start by one cycle
				*     Expect next transition in window after gap.
				*   Else, gap not verified, set CRANK_ERR_TOOTH_IN_GAP,
				*     set ENG_POS_SEEK and IRQ, signal output functions and
				*     restart searching for the gap  
				**************************************************************/
				/* calc tooth_period and record last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				/* verify gap (AB test) */
				if(f24i24mulu(crank_local.gap_ratio, tooth_period)
				   > crank_local.last_tooth_period)
				{ /* gap verified */
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					/* calculate an average tooth_period within the gap */
					crank_local.last_tooth_period_norm = tooth_period/(crank_local.teeth_in_gap + 1U);
					/* set TRR */
					CRANK_Set_TRR(crank_local.last_tooth_period_norm);
					/* set state - if the second tooth after the gap times out then
					   the state machine will revert to FIRST_TRANS */
					crank_local.state = CRANK_COUNTING_TIMEOUT;
					/* set tooth counters - first tooth after gap */
					crank_local.tooth_counter_gap = 1;
					tmp = crank_local.tooth_counter_cycle + crank_local.teeth_in_gap;
					while(++crank_local.tooth_counter_cycle <= tmp)
					{
						/* log average tooth period for all teeth in gap */
						CRANK_ToothArray_Log(crank_local.last_tooth_period_norm);
					}
#ifdef ERRATA_2477
					crank_local.err2477_tcr2_target +=
					  (crank_local.teeth_in_gap + 1U)*crank_local.tcr2_ticks_per_tooth;
#endif
					/* when the sync-cycle or engine-cycle is finished */
					switch(eng_pos_state)
					{
					case ENG_POS_FIRST_HALF_SYNC:
						/* if the sync cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_sync)
						{ /* It is time to ask the CPU to decode which half-cycle it was */
							/* set global eng_pos state and channel interrupt */
							eng_pos_state = ENG_POS_PRE_FULL_SYNC;
							channel.CIRC = CIRC_INT_FROM_SERVICED;
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* reset TCR2 */
							tcr2 = 0;
#ifdef ERRATA_2477
							crank_local.err2477_tcr2_target = 0;
#endif
						}
						break;
					case ENG_POS_PRE_FULL_SYNC:
						/* if the sync cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_sync)
						{ /* no answer from the CPU has been received during the whole
						     sync cycle */
							/* set global eng_pos state and channel interrupt */
							eng_pos_state = ENG_POS_FIRST_HALF_SYNC;
							channel.CIRC = CIRC_INT_FROM_SERVICED;
							/* reset Cam log */
							Link4(crank_local.link_cam);
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* reset TCR2 */
							tcr2 = 0;
#ifdef ERRATA_2477
							crank_local.err2477_tcr2_target = 0;
#endif
						}
						break;
					case ENG_POS_FULL_SYNC:
						/* if the engine cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_cycle)
						{
							/* set channel interrupt - once per cycle in full-sync */
#ifdef __ETPU2__
							channel.CIRC =  CIRC_BOTH_FROM_SERVICED;  /* on eTPU2, set also DMA request */
#else
							channel.CIRC =  CIRC_INT_FROM_SERVICED;
#endif
							/* reset Cam log */
							Link4(crank_local.link_cam);
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* increment eng_cycle_tcr2_start by one cycle */
							eng_cycle_tcr2_start += eng_cycle_tcr2_ticks;
						}
						break;
					}
					/* log tooth period (after possible tooth_counter_cycle reset) */
					CRANK_ToothArray_Log(crank_local.last_tooth_period_norm);
					/* open and close window using win_ratio_after_gap */
					CRANK_Window_NoReturn(crank_local.win_ratio_after_gap,
					                      crank_local.last_tooth_period_norm);
				}
				else
				{ /* gap not verified - an unexpected tooth in gap */
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* set error */
					crank_local.error |= CRANK_ERR_TOOTH_IN_GAP;
					/* restart searching for the gap */
					CRANK_Stall_NoReturn();
				}
				break;

			case CRANK_BLANK_TIME:
			case CRANK_TOOTH_BEFORE_GAP_NOT_HRM:
				/**************************************************************
				* STATE: T1, T10 - BLANK_TIME, TOOTH_BEFORE_GAP_NOT_HRM 
				* DESCRIPTION: 
				*   Transition detection should never happen in this state.
				*   Set CRANK_ERR_INVALID_TRANS.
				**************************************************************/
				crank_local.error |= CRANK_ERR_INVALID_TRANS;
				break;

			default:
				crank_local.error |= CRANK_ERR_INTERNAL;
				break;
			}
		}
		else /* cc.MRLB == 1 */
		{
			/* A timeout detected */
			channel.MRLB = MRL_CLEAR;
			switch(crank_local.state)
			{
			case CRANK_BLANK_TIME:
				/**************************************************************
				* STATE: M1 - BLANK_TIME
				* DESCRIPTION: 
				*   Blank_time after the first transition has passed.
				*   Start to detect transitions.
				**************************************************************/
				tcr2 = 0;
				if(cc.FM0 == CRANK_FM0_USE_TRANS_RISING)
				{
					channel.IPACA = IPAC_RISING;
				}
				else
				{
					channel.IPACA = IPAC_FALLING;
				}
				/* open window immediately, do not close it */
				erta = tcr1;
				channel.MRLA = MRL_CLEAR;
				channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
				/* set next state */
				crank_local.state = CRANK_BLANK_TEETH;
				if(crank_local.blank_teeth == 0)
				{
					crank_local.state = CRANK_FIRST_TRANS;
				}
				break;

			case CRANK_SECOND_TRANS:      /* first_tooth_timeout */
			case CRANK_TEST_POSSIBLE_GAP: /* long timeout over a possible gap */
			case CRANK_VERIFY_GAP:        /* win_ratio_after_gap */
				/**************************************************************
				* STATE: M4, M5, M6 - SECOND_TRANS, TEST_POSSIBLE_GAP, 
				*                     VERIFY_GAP
				* DESCRIPTION: 
				*   Transition not detected in a window, timeout happened 
				*   while gap is not verified.
				*   Set CRANK_ERR_TIMEOUT.
				*   Open the acceptance window immediately and do not close it.  
				**************************************************************/
				/* timeout happened while gap is not verified */
				tcr2 = 0;
				crank_local.error |= CRANK_ERR_TIMEOUT;
				crank_local.state = CRANK_FIRST_TRANS;
				/* open the acceptance window immediately and do not close it */
				erta = tcr1;
				channel.MRLA = MRL_CLEAR;
				channel.MRLB = MRL_CLEAR;
				channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
				break;

			case CRANK_COUNTING:          /* win_ratio_normal */
				/**************************************************************
				* STATE: M7 - COUNTING
				* DESCRIPTION: 
				*   Transition not detected in normal window, this is the first
				*   timeout, there has not been one immediately before.
				*   Set CRANK_ERR_TIMEOUT.
				*   Insert physical tooth, increment tooth counters.
				*   Expect next transition in window after timeout.  
				**************************************************************/
				crank_local.error |= CRANK_ERR_TIMEOUT;
				crank_local.state = CRANK_COUNTING_TIMEOUT;
				/* approximate when the missed tooth should have happened */
				tooth_period = crank_local.last_tooth_period;
				erta = crank_local.last_tooth_tcr1_time + tooth_period;
				crank_local.last_tooth_tcr1_time = erta;
				/* set IPH because one tooth was missing */
				tpr_str.IPH = 1;
#ifdef ERRATA_2477
				crank_local.err2477_tcr2_target += crank_local.tcr2_ticks_per_tooth;
#endif
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* test if before the gap */
				if(crank_local.tooth_counter_gap == crank_local.teeth_till_gap - 1)
				{
					/* there is one more teeth till the gap */
					crank_local.state = CRANK_TOOTH_BEFORE_GAP;
				}
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
				/* open and close window using win_ratio_after_timeout */
				CRANK_Window_NoReturn(crank_local.win_ratio_after_timeout, tooth_period);
				break;

			case CRANK_COUNTING_TIMEOUT:  /* win_ratio_after_timeout */
				/**************************************************************
				* STATE: M8 - COUNTING_TIMEOUT
				* DESCRIPTION: 
				*   Transition not detected in window after timeout, this is 
				*   the second timeout, there has been one immediately before.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_TOOTH_BEFORE_GAP:  /* win_ratio_normal */
				/**************************************************************
				* STATE: M9 - TOOTH_BEFORE_GAP
				* DESCRIPTION: 
				*   Transition not detected in normal window before gap.
				*   Set CRANK_ERR_TIMEOUT_BEFORE_GAP.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* set error */
				crank_local.error |= CRANK_ERR_TIMEOUT_BEFORE_GAP;
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_TOOTH_BEFORE_GAP_NOT_HRM:
				/**************************************************************
				* STATE: M10 - TOOTH_BEFORE_GAP_NOT_HRM
				* DESCRIPTION:
				*   On devices with ERRATA_2477, the MISSCNT must be written
				*   when EAC is out of high rate mode, which is now.
				*   Expect next transition within window across the gap.
				**************************************************************/
				/* write MISSCNT when not in High Rate Mode */
				/* set state */
				crank_local.state = CRANK_TOOTH_AFTER_GAP;
				/* write MISSCNT */
				tpr |= crank_local.misscnt_mask;
				/* open and close window using win_ratio_normal
				   and win_ratio_across_gap */
				channel.TBSB = TBS_M1C2GE;
				erta = crank_local.last_tooth_tcr1_time;
				CRANK_WindowAcrossGap_NoReturn(crank_local.last_tooth_period);
				break;

			case CRANK_TOOTH_AFTER_GAP:   /* long timeout over a possible gap */
				/**************************************************************
				* STATE: M11 - TOOTH_AFTER_GAP
				* DESCRIPTION: 
				*   Transition not detected in window across gap.
				*   Set CRANK_ERR_TIMEOUT_AFTER_GAP.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* set error */
				crank_local.error |= CRANK_ERR_TIMEOUT_AFTER_GAP;
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_SEEK:
			case CRANK_BLANK_TEETH:
			case CRANK_FIRST_TRANS:
				/**************************************************************
				* STATE: M0, M2, M3 - SEEK, BLANK_TEETH, FIRST_TRANS 
				* DESCRIPTION: 
				*   Match detection should never happen in this state.
				*   Set CRANK_ERR_INVALID_MATCH.
				**************************************************************/
				crank_local.error |= CRANK_ERR_INVALID_MATCH;
				break;

			default:
				crank_local.error |= CRANK_ERR_INTERNAL;
				break;
			}
		}
	}
	/**************************************************************************
	* THREAD NAME: CRANK_WITH_ADDITIONAL_TOOTH
	* DESCRIPTION: A transition or a timeout, handling a crank wheel with an
	*              additional tooth.
	**************************************************************************/
	else if((m1 == 1) && (m2 == 1) && (flag0 == CRANK_FLAG0_ADDITIONAL_TOOTH))
	{
		if(cc.TDLA == 1)
		{
			/* A tooth transition detected */
			channel.TDL = TDL_CLEAR;
			switch(crank_local.state)
			{
			case CRANK_SEEK:
				/**************************************************************
				* STATE: T0 - SEEK
				* DESCRIPTION: 
				*   First transition after INIT was detected.
				*   Wait for blank_time without detecting transitions.
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_BLANK_TIME;
				/* do not detect transitions */
				channel.IPACA = IPAC_NO_DETECT;
				/* keep window opened, close window after blank_time */
				CRANK_WindowCloseAt_NoReturn(erta + crank_local.blank_time);
				break;

			case CRANK_BLANK_TEETH:
				/**************************************************************
				* STATE: T2 - BLANK_TEETH
				* DESCRIPTION: 
				*   Downcount blank_teeth without tooth period measurement.
				**************************************************************/
				tcr2 = 0;
				/* downcount blank_teeth */
				if(--crank_local.blank_teeth <= 0)
				{
					/* set_state */
					crank_local.state = CRANK_FIRST_TRANS;
				}
				break;

			case CRANK_FIRST_TRANS:
				/**************************************************************
				* STATE: T3 - FIRST_TRANS
				* DESCRIPTION: 
				*   First transition after blank_teeth was detected.
				*   Record transition time.
				*   Next transition is expected within first_tooth_timeout. 
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_SECOND_TRANS;
				/* record last_tooth_tcr1_time */
				crank_local.last_tooth_tcr1_time = erta;
				/* keep window opened, close window after first_tooth_timeout */
				CRANK_WindowCloseAt_NoReturn(erta + crank_local.first_tooth_timeout);
				break;

			case CRANK_SECOND_TRANS:
				/**************************************************************
				* STATE: T4A - SECOND_TRANS
				* DESCRIPTION: 
				*   Second transition after blank_teeth was detected.
				*   Calculate tooth period and record transition time.
				*   Next transition is expected in normal window or earlier. 
				**************************************************************/
				tcr2 = 0;
				/* set_state */
				crank_local.state = CRANK_TEST_POSSIBLE_GAP;
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				/* keep window opened, close using win_ratio_normal */
				CRANK_WindowClose_NoReturn(crank_local.win_ratio_normal,
				                           tooth_period);
				break;

			case CRANK_TEST_POSSIBLE_GAP:
				/**************************************************************
				* STATE: T5A - TEST_POSSIBLE_GAP
				* DESCRIPTION: 
				*   Transition detected, no synchronization yet.
				*   Calculate tooth period and record transition time.
				*   Test for a possible gap (AB of ABA test).
				*   If possible gap found, expect next transition in normal 
				*     window from the previous tooth (not the additional one).
				*   Else, expect next transition in normal window or earlier.
				**************************************************************/
				tcr2 = 0;
				/* calc tooth_period */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				/* test for a possible additional tooth (AB test) */
				if(f24i24mulu(crank_local.gap_ratio, crank_local.last_tooth_period)
				   > tooth_period)
				{ /* a possible additional tooth found */
					/* record additional_tooth_period */
					crank_local.additional_tooth_period = tooth_period;
					/* set state */
					crank_local.state = CRANK_VERIFY_GAP;
					/* open and close window using win_ratio_normal from 
					   the previous tooth (not the additional one) */
					half_window_width = f24i24mulu(crank_local.win_ratio_normal,
					                               crank_local.last_tooth_period);
					CRANK_WindowCloseAt_NoReturn(crank_local.last_tooth_tcr1_time
							+ crank_local.last_tooth_period + half_window_width);
				}
				else
				{ /* gap not found */
					/* record last_tooth_tcr1_time */
					crank_local.last_tooth_tcr1_time = erta;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* keep window opened, close using win_ratio_normal */
					CRANK_WindowClose_NoReturn(crank_local.win_ratio_normal,
					                           tooth_period);
				}
				break;

			case CRANK_VERIFY_GAP:
				/**************************************************************
				* STATE: T6A - VERIFY_GAP
				* DESCRIPTION: 
				*   Transition detected in window, after an additional tooth.
				*   Calculate tooth period and record transition time.
				*   Verify a possible gap (BA of ABA test).
				*   If gap verified, this is the first tooth after gap,
				*     start TCR2, set ENG_POS_FIRST_HALF_SYNC and IRQ,
				*     reset Cam log, expect next transition in normal window. 
				*   Else, test possible gap again, expect next transition  
				*     in normal window or earlier.
				**************************************************************/
				/* calc tooth_period and record last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				/* verify a possible additional tooth (BA portion of the ABA test) */
				if(f24i24mulu(crank_local.gap_ratio, tooth_period)
				   > crank_local.additional_tooth_period)
				{ /* additional tooth verified */
					/* set states */
					crank_local.state = CRANK_COUNTING;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* set tooth counters - the first tooth after gap */
					crank_local.tooth_counter_gap = 1;
					crank_local.tooth_counter_cycle = 1;
					/* set TRR and TICKS */
					tpr_str.TICKS = (uint16_t)crank_local.tcr2_ticks_per_tooth - 1;
					CRANK_Set_TRR(tooth_period);
					tcr2 = 0;
					/* set global eng_pos state and channel interrupt */
					eng_pos_state = ENG_POS_FIRST_HALF_SYNC;
					channel.CIRC = CIRC_INT_FROM_SERVICED;
					/* reset Cam log */
					Link4(crank_local.link_cam);
					/* open and close window using win_ratio_normal */
					CRANK_Window_NoReturn(crank_local.win_ratio_normal, tooth_period);
				}
				else
				{ /* additional tooth not verified */
					/* set state */
					crank_local.state = CRANK_TEST_POSSIBLE_GAP;
					/* correct tooth_period - it was not the additional tooth */
					tooth_period -= crank_local.additional_tooth_period;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* keep window opened, close using win_ratio_normal */
					CRANK_WindowClose_NoReturn(crank_local.win_ratio_normal,
					                           tooth_period);
				}
				break;

			case CRANK_COUNTING_TIMEOUT:
				/**************************************************************
				* STATE: T8 - COUNTING_TIMEOUT
				* DESCRIPTION: 
				*   Transition detected within window after a single timeout.
				*   Recover and continue normal counting.
				**************************************************************/
				/* recover from counting timeout */
				/* set state */
				crank_local.state = CRANK_COUNTING;
				/* continue to normal processing at CRANK_COUNTING */

			case CRANK_COUNTING:
				/**************************************************************
				* STATE: T7 - COUNTING
				* DESCRIPTION: 
				*   Transition detected in normal window.
				*   Calculate tooth period and record transition time.
				*   Increment tooth counters.
				*   Check if the next tooth is the last before gap.
				*   Adjust TCR2 rate.
				*   Expect next transition in normal window. 
				**************************************************************/
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				crank_local.last_tooth_period_norm = tooth_period; 
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* test if before the gap */
				if(crank_local.tooth_counter_gap == crank_local.teeth_till_gap - 1)
				{
					/* there is one more teeth till the gap */
					crank_local.state = CRANK_TOOTH_BEFORE_GAP;
				}
				/* set TRR */
				CRANK_Set_TRR(tooth_period);
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
				/* open and close window using win_ratio_normal */
				CRANK_Window_NoReturn(crank_local.win_ratio_normal, tooth_period);
				break;

			case CRANK_TOOTH_BEFORE_GAP:
				/**************************************************************
				* STATE: T9A - TOOTH_BEFORE_GAP
				* DESCRIPTION: 
				*   Transition detected in normal window, gap expected next.
				*   Calculate tooth period and record transition time.
				*   Increment tooth counters.
				*   Adjust TCR2 rate.
				*   Expect next transition in normal window or earlier. 
				**************************************************************/
				/* record last_tooth_period and last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				crank_local.last_tooth_period = tooth_period;
				crank_local.last_tooth_period_norm = tooth_period; 
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* Set TICKS */
				tpr_str.TICKS = (uint16_t)crank_local.tcr2_ticks_per_add_tooth - 1;
				/* set TRR */
				CRANK_Set_TRR(tooth_period);
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
				/* set state */
				crank_local.state = CRANK_ADDITIONAL_TOOTH;
				/* keep window opened, close using win_ratio_normal */
				CRANK_WindowClose_NoReturn(crank_local.win_ratio_normal,
				                           tooth_period);
				break;

			case CRANK_ADDITIONAL_TOOTH:
				/**************************************************************
				* STATE: T10A - CRANK_ADDITIONAL_TOOTH
				* DESCRIPTION: 
				*   Transition detected in additional-tooth window.
				*   Calculate additional tooth period.
				*   Verify the additional tooth (AB of ABA test).
				*   If tooth verified, expect next transition within normal 
				*     window from the last tooth (not the additional tooth). 
				*   Else, tooth not verified, set CRANK_ERR_ADD_TOOTH_NOT_FOUND,
				*     set ENG_POS_SEEK and IRQ, signal output functions and
				*     restart searching for the gap  
				**************************************************************/
				/* calc tooth_period */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				/* Set TICKS */
				tpr_str.TICKS = (uint16_t)(crank_local.tcr2_ticks_per_tooth - crank_local.tcr2_ticks_per_add_tooth) - 1;
				/* verify the additional tooth (AB test) */
				if(f24i24mulu(crank_local.gap_ratio, crank_local.last_tooth_period)
				   > tooth_period)
				{ /* additional tooth not verified */
					/* record additional_tooth_period */
					crank_local.additional_tooth_period = tooth_period;
					/* set state */
					crank_local.state = CRANK_TOOTH_AFTER_GAP;
					/* open and close window using win_ratio_normal from 
					   the previous tooth (not the additional one) */
					half_window_width = f24i24mulu(crank_local.win_ratio_normal,
					                               crank_local.last_tooth_period);
					CRANK_WindowCloseAt_NoReturn(crank_local.last_tooth_tcr1_time
							+ crank_local.last_tooth_period + half_window_width);
				}
				else
				{ /* additional tooth not verified */
					/* record last_tooth_tcr1_time */
					crank_local.last_tooth_tcr1_time = erta;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* set error */
					crank_local.error |= CRANK_ERR_ADD_TOOTH_NOT_FOUND;
					/* restart searching for the gap */
					CRANK_Stall_NoReturn();
				}
				break;

			case CRANK_TOOTH_AFTER_GAP:
				/**************************************************************
				* STATE: T11A - TOOTH_AFTER_GAP
				* DESCRIPTION: 
				*   Transition detected in normal window from the last tooth.
				*   Calculate tooth period and record transition time.
				*   Verify the additional tooth (BA of ABA test).
				*   If additional tooth verified, adjust TCR2 rate and tooth
				*     counters, sync-cycle or engine-cycle is finished:
				*     In ENG_POS_FIRST_HALF_SYNC, 
				*       ask CPU to decode the Cam log, 
				*       reset TCR2, reset tooth_counter_cycle,
				*       set ENG_POS_PRE_FULL_SYNC and IRQ.
				*     In ENG_POS_PRE_FULL_SYNC, there was no response from CPU,
				*       reset Cam log, reset TCR2, reset tooth_counter_cycle,
				*       set ENG_POS_FIRST_HALF_SYNC and IRQ.
				*     In ENG_POS_FULL_SYNC,
				*       reset Cam log, reset tooth_counter_cycle, 
				*       set IRQ (once per cycle in full sync)
				*       increment eng_cycle_tcr2_start by one cycle
				*     Expect next transition in normal window.
				*   Else, additional tooth not verified, set CRANK_ERR_TOOTH_IN_GAP,
				*     set ENG_POS_SEEK and IRQ, signal output functions and
				*     restart searching for the gap  
				**************************************************************/
				/* Set TICKS */
				tpr_str.TICKS = (uint16_t)crank_local.tcr2_ticks_per_tooth - 1;
				/* calc tooth_period and record last_tooth_tcr1_time */
				tooth_period = erta - crank_local.last_tooth_tcr1_time;
				crank_local.last_tooth_tcr1_time = erta;
				/* verify a possible additional tooth (BA portion of the ABA test) */
				if(f24i24mulu(crank_local.gap_ratio, tooth_period)
				   > crank_local.additional_tooth_period)
				{ /* additional tooth verified */
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* set TRR */
					CRANK_Set_TRR(tooth_period);
					/* set state - if the second tooth after the gap times out then
					   the state machine will revert to FIRST_TRANS */
					crank_local.state = CRANK_COUNTING_TIMEOUT;
					/* set tooth counters - first tooth after gap */
					crank_local.tooth_counter_gap = 1;
					crank_local.tooth_counter_cycle++;
					/* log tooth period */
					CRANK_ToothArray_Log(tooth_period);
					/* when the sync-cycle or engine-cycle is finished */
					switch(eng_pos_state)
					{
					case ENG_POS_FIRST_HALF_SYNC:
						/* if the sync cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_sync)
						{ /* It is time to ask the CPU to decode which half-cycle it was */
							/* set global eng_pos state and channel interrupt */
							eng_pos_state = ENG_POS_PRE_FULL_SYNC;
							channel.CIRC = CIRC_INT_FROM_SERVICED;
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* reset TCR2 */
							tcr2 = 0;
						}
						break;
					case ENG_POS_PRE_FULL_SYNC:
						/* if the sync cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_sync)
						{ /* no answer from the CPU has been received during the whole
						     sync cycle */
							/* set global eng_pos state and channel interrupt */
							eng_pos_state = ENG_POS_FIRST_HALF_SYNC;
							channel.CIRC = CIRC_INT_FROM_SERVICED;
							/* reset Cam log */
							Link4(crank_local.link_cam);
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* reset TCR2 */
							tcr2 = 0;
						}
						break;
					case ENG_POS_FULL_SYNC:
						/* if the engine cycle is finished */
						if(crank_local.tooth_counter_cycle >= crank_local.teeth_per_cycle)
						{
							/* set channel interrupt - once per cycle in full-sync */
#ifdef __ETPU2__
							channel.CIRC =  CIRC_BOTH_FROM_SERVICED;  /* on eTPU2, set also DMA request */
#else
							channel.CIRC =  CIRC_INT_FROM_SERVICED;
#endif
							/* reset Cam log */
							Link4(crank_local.link_cam);
							/* reset tooth_counter_cycle */
							crank_local.tooth_counter_cycle = 1;
							/* increment eng_cycle_tcr2_start by one cycle */
							eng_cycle_tcr2_start += eng_cycle_tcr2_ticks;
						}
						break;
					}
					/* open and close window using win_ratio_normal */
					CRANK_Window_NoReturn(crank_local.win_ratio_normal, tooth_period);
				}
				else
				{ /* additional tooth not verified */
					/* correct tooth_period - it was not the additional tooth */
					tooth_period -= crank_local.additional_tooth_period;
					/* record last_tooth_period */
					crank_local.last_tooth_period = tooth_period;
					crank_local.last_tooth_period_norm = tooth_period; 
					/* set error */
					crank_local.error |= CRANK_ERR_ADD_TOOTH_NOT_FOUND;
					/* restart searching for the gap */
					CRANK_Stall_NoReturn();
				}
				break;

			case CRANK_BLANK_TIME:
				/**************************************************************
				* STATE: T1 - BLANK_TIME 
				* DESCRIPTION: 
				*   Transition detection should never happen in this state.
				*   Set CRANK_ERR_INVALID_TRANS.
				**************************************************************/
				crank_local.error |= CRANK_ERR_INVALID_TRANS;
				break;

			default:
				crank_local.error |= CRANK_ERR_INTERNAL;
				break;
			}
		}
		else /* cc.MRLB == 1 */
		{
			/* A timeout detected */
			channel.MRLB = MRL_CLEAR;
			switch(crank_local.state)
			{
			case CRANK_BLANK_TIME:
				/**************************************************************
				* STATE: M1 - BLANK_TIME
				* DESCRIPTION: 
				*   Blank_time after the first transition has passed.
				*   Start to detect transitions.
				**************************************************************/
				tcr2 = 0;
				if(cc.FM0 == CRANK_FM0_USE_TRANS_RISING)
				{
					channel.IPACA = IPAC_RISING;
				}
				else
				{
					channel.IPACA = IPAC_FALLING;
				}
				/* open window immediately, do not close it */
				erta = tcr1;
				channel.MRLA = MRL_CLEAR;
				channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
				/* set next state */
				crank_local.state = CRANK_BLANK_TEETH;
				if(crank_local.blank_teeth == 0)
				{
					crank_local.state = CRANK_FIRST_TRANS;
				}
				break;

			case CRANK_SECOND_TRANS:      /* first_tooth_timeout */
			case CRANK_TEST_POSSIBLE_GAP: /* win_ratio_normal */
			case CRANK_VERIFY_GAP:        /* win_ratio_normal */
				/**************************************************************
				* STATE: M4, M5, M6 - SECOND_TRANS, TEST_POSSIBLE_GAP, 
				*                     VERIFY_GAP
				* DESCRIPTION: 
				*   Transition not detected in a window, timeout happened 
				*   while gap is not verified.
				*   Set CRANK_ERR_TIMEOUT.
				*   Open the acceptance window immediately and do not close it.  
				**************************************************************/
				/* timeout happened while gap is not verified */
				tcr2 = 0;
				crank_local.error |= CRANK_ERR_TIMEOUT;
				crank_local.state = CRANK_FIRST_TRANS;
				/* open the acceptance window immediately and do not close it */
				erta = tcr1;
				channel.MRLA = MRL_CLEAR;
				channel.MRLB = MRL_CLEAR;
				channel.ERWA = ERW_WRITE_ERT_TO_MATCH;
				break;

			case CRANK_COUNTING:          /* win_ratio_normal */
				/**************************************************************
				* STATE: M7 - COUNTING
				* DESCRIPTION: 
				*   Transition not detected in normal window, this is the first
				*   timeout, there has not been one immediately before.
				*   Set CRANK_ERR_TIMEOUT.
				*   Insert physical tooth, increment tooth counters.
				*   Expect next transition in window after timeout.  
				**************************************************************/
				crank_local.error |= CRANK_ERR_TIMEOUT;
				crank_local.state = CRANK_COUNTING_TIMEOUT;
				/* approximate when the missed tooth should have happened */
				tooth_period = crank_local.last_tooth_period;
				erta = crank_local.last_tooth_tcr1_time + tooth_period;
				crank_local.last_tooth_tcr1_time = erta;
				/* set IPH because one tooth was missing */
				tpr_str.IPH = 1;
				/* increment tooth counters */
				crank_local.tooth_counter_gap++;
				crank_local.tooth_counter_cycle++;
				/* test if before the gap */
				if(crank_local.tooth_counter_gap == crank_local.teeth_till_gap - 1)
				{
					/* there is one more teeth till the gap */
					crank_local.state = CRANK_TOOTH_BEFORE_GAP;
				}
				/* log tooth period */
				CRANK_ToothArray_Log(tooth_period);
				/* open and close window using win_ratio_after_timeout */
				CRANK_Window_NoReturn(crank_local.win_ratio_after_timeout, tooth_period);
				break;

			case CRANK_COUNTING_TIMEOUT:  /* win_ratio_after_timeout */
				/**************************************************************
				* STATE: M8 - COUNTING_TIMEOUT
				* DESCRIPTION: 
				*   Transition not detected in window after timeout, this is 
				*   the second timeout, there has been one immediately before.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_TOOTH_BEFORE_GAP:  /* win_ratio_normal */
				/**************************************************************
				* STATE: M9 - TOOTH_BEFORE_GAP
				* DESCRIPTION: 
				*   Transition not detected in normal window before gap.
				*   Set CRANK_ERR_TIMEOUT_BEFORE_GAP.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* set error */
				crank_local.error |= CRANK_ERR_TIMEOUT_BEFORE_GAP;
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_ADDITIONAL_TOOTH:   /* win_ratio_normal */
				/**************************************************************
				* STATE: M10A - CRANK_ADDITIONAL_TOOTH
				* DESCRIPTION:
				**************************************************************/
				/* set error */
				crank_local.error |= CRANK_ERR_ADD_TOOTH_NOT_FOUND;
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_TOOTH_AFTER_GAP:   /* win_ratio_normal */
				/**************************************************************
				* STATE: M11 - TOOTH_AFTER_GAP
				* DESCRIPTION: 
				*   Transition not detected in window across gap.
				*   Set CRANK_ERR_TIMEOUT_AFTER_GAP.
				*   Set ENG_POS_SEEK and IRQ, signal output functions and
				*   restart searching for the gap.  
				**************************************************************/
				/* set error */
				crank_local.error |= CRANK_ERR_TIMEOUT_AFTER_GAP;
				/* restart searching for the gap */
				CRANK_Stall_NoReturn();
				break;

			case CRANK_SEEK:
			case CRANK_BLANK_TEETH:
			case CRANK_FIRST_TRANS:
				/**************************************************************
				* STATE: M0, M2, M3 - SEEK, BLANK_TEETH, FIRST_TRANS 
				* DESCRIPTION: 
				*   Match detection should never happen in this state.
				*   Set CRANK_ERR_INVALID_MATCH.
				**************************************************************/
				crank_local.error |= CRANK_ERR_INVALID_MATCH;
				break;

			default:
				crank_local.error |= CRANK_ERR_INTERNAL;
				break;
			}
		}
	}
	/*******************************************************************************
	* THREAD NAME: UNHANDLED EVENTS
	*******************************************************************************/
	else
	{
		GlobalError();
	}
}
/*******************************************************************************
*  Export interface information to Host CPU program.
*******************************************************************************/
/* [MISRA 2004 Rule 3.4] usage of #pragma write documented in 
   eTPU_Build_Tools_Reference.pdf, v.09/2013, chapter 26.13, p.309 */
#pragma write h, (::ETPUfilename (cpu/etpu_crank_auto.h));
#pragma write h, (/**************************************************************** );
#pragma write h, (* WARNING: This file is automatically generated. DO NOT EDIT IT! );
#pragma write h, (*);
#pragma write h, (* COPYRIGHT (c) Freescale 2004-2014, All Rights Reserved );
#pragma write h, (*);
#pragma write h, (* FILE NAME: etpu_crank_auto.h  );
#ifdef __ETPU2__
#pragma write h, (* ARCHITECTURE: eTPU2 );
#else
#pragma write h, (* ARCHITECTURE: eTPU );
#endif
#pragma write h, (*);
#pragma write h, (* This file was generated by: __FILE__ on __DATE__, __TIME__ );
#pragma write h, (*);
#pragma write h, (* This file provides an interface between eTPU code and CPU       );
#pragma write h, (* code. All references to the eTPU function should be made with   );
#pragma write h, (* information in this file. This allows only symbolic             );
#pragma write h, (* information to be referenced which allows the eTPU code to be   );
#pragma write h, (* optimized without effecting the CPU code.                       );
#pragma write h, (*****************************************************************/);
#pragma write h, (#ifndef _ETPU_CRANK_AUTO_H_ );
#pragma write h, (#define _ETPU_CRANK_AUTO_H_ );
#pragma write h, ( );
#pragma write h, (/* Function Configuration Information */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FUNCTION_NUMBER) ::ETPUfunctionnumber(CRANK) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_TABLE_SELECT) ::ETPUentrytype(CRANK) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_NUM_PARMS) ::ETPUram(CRANK) );
#pragma write h, ( );
#pragma write h, (/* Host Service Request Definitions */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_HSR_INIT)         CRANK_HSR_INIT );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_HSR_SET_SYNC)     CRANK_HSR_SET_SYNC );
#pragma write h, ( );
#pragma write h, (/* Function Mode Bit Definitions */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FM0_USE_TRANS_RISING)  CRANK_FM0_USE_TRANS_RISING );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FM0_USE_TRANS_FALLING) CRANK_FM0_USE_TRANS_FALLING );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FM1_TOOTH_PERIODS_LOG_OFF) (0) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FM1_TOOTH_PERIODS_LOG_ON)  (CRANK_FM1_LOG_TOOTH_PERIODS << 1) );
#pragma write h, ( );
#pragma write h, (/* Parameter Definitions */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_BLANK_TIME              ) ::ETPUlocation (CRANK, crank_local.blank_time              ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TCR2_TICKS_PER_TOOTH    ) ::ETPUlocation (CRANK, crank_local.tcr2_ticks_per_tooth    ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TCR2_TICKS_PER_ADD_TOOTH) ::ETPUlocation (CRANK, crank_local.tcr2_ticks_per_add_tooth) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LAST_TOOTH_TCR1_TIME    ) ::ETPUlocation (CRANK, crank_local.last_tooth_tcr1_time    ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LAST_TOOTH_PERIOD       ) ::ETPUlocation (CRANK, crank_local.last_tooth_period       ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LAST_TOOTH_PERIOD_NORM  ) ::ETPUlocation (CRANK, crank_local.last_tooth_period_norm  ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_ADDITIONAL_TOOTH_PERIOD ) ::ETPUlocation (CRANK, crank_local.additional_tooth_period ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TCR2_ADJUSTMENT         ) ::ETPUlocation (CRANK, crank_local.tcr2_adjustment         ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_GAP_RATIO               ) ::ETPUlocation (CRANK, crank_local.gap_ratio               ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_WIN_RATIO_NORMAL        ) ::ETPUlocation (CRANK, crank_local.win_ratio_normal        ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_WIN_RATIO_ACROSS_GAP    ) ::ETPUlocation (CRANK, crank_local.win_ratio_across_gap    ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_WIN_RATIO_AFTER_GAP     ) ::ETPUlocation (CRANK, crank_local.win_ratio_after_gap     ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_WIN_RATIO_AFTER_TIMEOUT ) ::ETPUlocation (CRANK, crank_local.win_ratio_after_timeout ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_FIRST_TOOTH_TIMEOUT     ) ::ETPUlocation (CRANK, crank_local.first_tooth_timeout     ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LINK_CAM                ) ::ETPUlocation (CRANK, crank_local.link_cam                ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LINK_1                  ) ::ETPUlocation (CRANK, crank_local.link_1                  ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LINK_2                  ) ::ETPUlocation (CRANK, crank_local.link_2                  ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LINK_3                  ) ::ETPUlocation (CRANK, crank_local.link_3                  ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_LINK_4                  ) ::ETPUlocation (CRANK, crank_local.link_4                  ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TEETH_TILL_GAP          ) ::ETPUlocation (CRANK, crank_local.teeth_till_gap          ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TEETH_IN_GAP            ) ::ETPUlocation (CRANK, crank_local.teeth_in_gap            ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_MISSCNT_MASK            ) ::ETPUlocation (CRANK, crank_local.misscnt_mask            ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TEETH_PER_CYCLE         ) ::ETPUlocation (CRANK, crank_local.teeth_per_cycle         ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TEETH_PER_SYNC          ) ::ETPUlocation (CRANK, crank_local.teeth_per_sync          ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TOOTH_COUNTER_GAP       ) ::ETPUlocation (CRANK, crank_local.tooth_counter_gap       ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TOOTH_COUNTER_CYCLE     ) ::ETPUlocation (CRANK, crank_local.tooth_counter_cycle     ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_BLANK_TEETH             ) ::ETPUlocation (CRANK, crank_local.blank_teeth             ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_STATE                   ) ::ETPUlocation (CRANK, crank_local.state                   ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_ERROR                   ) ::ETPUlocation (CRANK, crank_local.error                   ) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_TOOTH_PERIOD_LOG        ) ::ETPUlocation (CRANK, crank_local.tooth_period_log        ) );
#ifdef ERRATA_2477
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_OFFSET_ERR2477_TCR2_TARGET     ) ::ETPUlocation (CRANK, crank_local.err2477_tcr2_target     ) );
#endif
#pragma write h, ( );
#pragma write h, (/* Globals definitions */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_OFFSET_ENG_POS_STATE                 )  ::ETPUlocation (eng_pos_state) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_OFFSET_ENG_CYCLE_TCR2_TICKS          )  ::ETPUlocation (eng_cycle_tcr2_ticks) );
#pragma write h, (::ETPUliteral(#define FS_ETPU_OFFSET_ENG_CYCLE_TCR2_START          )  ::ETPUlocation (eng_cycle_tcr2_start) );
#pragma write h, ( );
#pragma write h, (/* Errors */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_NO_ERROR           ) CRANK_ERR_NO_ERROR           );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_INVALID_TRANS      ) CRANK_ERR_INVALID_TRANS      );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_INVALID_MATCH      ) CRANK_ERR_INVALID_MATCH      );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_TIMEOUT            ) CRANK_ERR_TIMEOUT            );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_STALL              ) CRANK_ERR_STALL              );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_INTERNAL           ) CRANK_ERR_INTERNAL           );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_TIMEOUT_BEFORE_GAP ) CRANK_ERR_TIMEOUT_BEFORE_GAP );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_TIMEOUT_AFTER_GAP  ) CRANK_ERR_TIMEOUT_AFTER_GAP  );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_TOOTH_IN_GAP       ) CRANK_ERR_TOOTH_IN_GAP       );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ERR_ADD_TOOTH_NOT_FOUND) CRANK_ERR_ADD_TOOTH_NOT_FOUND);
#pragma write h, ( );
#pragma write h, (/* Crank State values */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_SEEK                     ) CRANK_SEEK                     );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_BLANK_TIME               ) CRANK_BLANK_TIME               );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_BLANK_TEETH              ) CRANK_BLANK_TEETH              );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_FIRST_TRANS              ) CRANK_FIRST_TRANS              );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_SECOND_TRANS             ) CRANK_SECOND_TRANS             );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_TEST_POSSIBLE_GAP        ) CRANK_TEST_POSSIBLE_GAP        );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_VERIFY_GAP               ) CRANK_VERIFY_GAP               );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_COUNTING                 ) CRANK_COUNTING                 );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_COUNTING_TIMEOUT         ) CRANK_COUNTING_TIMEOUT         );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_TOOTH_BEFORE_GAP         ) CRANK_TOOTH_BEFORE_GAP         );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_TOOTH_BEFORE_GAP_NOT_HRM ) CRANK_TOOTH_BEFORE_GAP_NOT_HRM );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_ADDITIONAL_TOOTH         ) CRANK_ADDITIONAL_TOOTH );
#pragma write h, (::ETPUliteral(#define FS_ETPU_CRANK_TOOTH_AFTER_GAP          ) CRANK_TOOTH_AFTER_GAP          );
#pragma write h, ( );
#pragma write h, (/* Global Engine Position State values */);
#pragma write h, (::ETPUliteral(#define FS_ETPU_ENG_POS_SEEK            ) ENG_POS_SEEK            );
#pragma write h, (::ETPUliteral(#define FS_ETPU_ENG_POS_FIRST_HALF_SYNC ) ENG_POS_FIRST_HALF_SYNC );
#pragma write h, (::ETPUliteral(#define FS_ETPU_ENG_POS_PRE_FULL_SYNC   ) ENG_POS_PRE_FULL_SYNC   );
#pragma write h, (::ETPUliteral(#define FS_ETPU_ENG_POS_FULL_SYNC       ) ENG_POS_FULL_SYNC       );
#pragma write h, ( );
#pragma write h, (#endif );

/*********************************************************************
 *
 * Copyright:
 *	Freescale Semiconductor, INC. All Rights Reserved.
 *  You are hereby granted a copyright license to use, modify, and
 *  distribute the SOFTWARE so long as this entire notice is
 *  retained without alteration in any modified and/or redistributed
 *  versions, and that such modified versions are clearly identified
 *  as such. No licenses are granted by implication, estoppel or
 *  otherwise under any patents or trademarks of Freescale
 *  Semiconductor, Inc. This software is provided on an "AS IS"
 *  basis and without warranty.
 *
 *  To the maximum extent permitted by applicable law, Freescale
 *  Semiconductor DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED,
 *  INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
 *  PARTICULAR PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT WITH
 *  REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF)
 *  AND ANY ACCOMPANYING WRITTEN MATERIALS.
 *
 *  To the maximum extent permitted by applicable law, IN NO EVENT
 *  SHALL Freescale Semiconductor BE LIABLE FOR ANY DAMAGES WHATSOEVER
 *  (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
 *  BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER
 *  PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE.
 *
 *  Freescale Semiconductor assumes no responsibility for the
 *  maintenance and support of this software
 ********************************************************************/

/*******************************************************************************
*
*  REVISION HISTORY:
*
*  FILE OWNER: Milan Brejl [r54529]
*  Revision 1.2  2015/09/01  r54529
*  Output parameter last_tooth_period_norm added.
*
*  Revision 1.11  2015/06/29  r54529
*  eTPU compiler 10.2.2, minor change at line 579.
*
*  Revision 1.1  2014/10/27  r54529
*  Crank with an additional tooth generated (teeth_till_gap + 1)*tcr2_ticks_per_tooth ticks per 360deg.
*  Fixed, parameter tcr2_ticks_per_add_tooth added.
*
*  Revision 1.0  2014/03/16  r54529
*  Bug fix - tooth_period_norm used for TRR calculation on tooth after gap.
*  Minor comment and formating improvements. MISRA compliance checked.
*  Ready for eTPU Engine Control Library release 1.0.
*
*  Revision 0.3  2013/11/27  r54529
*  Full precision of TRR calculation.
*  MISSCNT[2] supported - up to 7 missing teeth. teeth_in_gap replaced by misscnt_mask.  
*  
*  Revision 0.2  2013/08/14  r54529
*  TCR2 not reset on stall.
*
*  Revision 0.1  2012/06/12  r54529
*  Initial version.
*
*******************************************************************************/
