/** Internal defines and inline code for schip's MSCapture function library.
 *   Stolen from and based on the Arduinoish FreqMeasure library:
 *       http://www.pjrc.com/teensy/td_libs_FreqMeasure.html
 *       Copyright (c) 2011 PJRC.COM, LLC - Paul Stoffregen <paul@pjrc.com>
 *       Version 1.0
 * Further copy wronged by M. Schippling 11/15 schip@etantdonnes.com
 * Version 1.0
 *
 * Note: Only ported to ATmega 328p on Uno and ProMini
 * 			The rest is an exercise for the abuser.
 */
#ifndef capture_h_
#define capture_h_

#include <avr/io.h>
#include <avr/interrupt.h>

// figure out which chip we are using and define what is what
//  Note: UNO and ProMini can only use pin D8 as input
//   others as noted in specific defines

// Arduino Uno, Duemilanove, LilyPad, Mini, Fio, etc
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
  #define CAPTURE_USE_TIMER1       // ICP1 is pin 8

// Teensy 1.0
#elif defined(__AVR_AT90USB162__)
  #define CAPTURE_USE_TIMER1       // ICP1 is pin 16

// Teensy 2.0
#elif defined(__AVR_ATmega32U4__)
  // #define CAPTURE_USE_TIMER1    // ICP1 is pin 22
  #define CAPTURE_USE_TIMER3       // ICP3 is pin 10

// Teensy++ 1.0 & 2.0
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
  // #define CAPTURE_USE_TIMER1    // ICP1 is pin 4
  #define CAPTURE_USE_TIMER3       // ICP3 is pin 17

// Sanguino
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
  #define CAPTURE_USE_TIMER1       // ICP1 is pin 14

// Arduino Mega
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // #define CAPTURE_USE_TIMER1    // ICP1 is not connected
  // #define CAPTURE_USE_TIMER3    // ICP3 is not connected
  #define CAPTURE_USE_TIMER4       // ICP4 is pin 49
  // #define CAPTURE_USE_TIMER5    // ICP5 is pin 48

#else
  #error "Unknown chip, please edit me with timer+counter definitions"

#endif


// this section is for UNO and PROMini
#if defined(CAPTURE_USE_TIMER1)

// variables to keep original register settings
//  in case we want to revert while running... unlikely, but...
static uint8_t saveTCCR1A, saveTCCR1B;

// set Timer1 registers as noted
//  todo: add arguments to set capture mode and timer prescale 
static inline void capture_init(void)
{
	// save current control reg settings in case we revert
	saveTCCR1A = TCCR1A;
	saveTCCR1B = TCCR1B;
	// clear control registers
	// COM bits in TCCR1A are all 0 -- "normal", non-compare, operation
	// WGM bits in both TCCR1A,B are all 0 -- "normal", non-PWM operation
	TCCR1B = 0;
	TCCR1A = 0;
	// clear Timer counting register
	TCNT1 = 0;
	// clear interrupt register, just in case
	//  clear ICF1 (capture int flag) and TOV1 (overflow int flag)
	TIFR1 = (1<<ICF1) | (1<<TOV1);
	// enable timer interrupts
	//  set ICIE1 (capture int) and TOIE1 (overflow int)
	TIMSK1 = (1<<ICIE1) | (1<<TOIE1);
}

// start em up
//  todo: modify clock-prescale CS12,CS11,CS10 (bits2,1,0) as requested:
//  		001b -- no-prescale (system clock)	-- 16Mhz - 0.0625uS
//          010b -- CKL/8						-- 2Mhz - 0.5uS
//          011b -- CKL/64						-- 250Khz - 4uS
//          100b -- CKL/256						-- 62.5KHz - 16uS
//          101b -- CKL/1024					-- 15.625Khz - 64uS
//  todo: modify edge select as per operation mode
//  		0xC0 - normal period count between positive edge transitions
//  		0x40 - period count of HIGH pulse
//  		0x80 - period count of LOW pulse
//   note: both of above are special values to match 328p TCCR1B register
//   		CLK pre-scale select in bit0,1,2 positions
//  		ICES1 edge setting in bit6 position
static inline void capture_start( uint8_t prescale, uint8_t mode )
{
	// set ICNC1 (capture noise canceler) ON
	// set ICES1 (capture edge select)) to rising edge
	// set CS12,CS11,CS10 (clock pre-scale) to 001b -- no pre-scale
	// WGM bits in both TCCR1A,B are all 0 -- "normal", non-PWM operation
//	TCCR1B = (1<<ICNC1) | (1<<ICES1) | (1<<CS10); // no-prescale, pos edge

	// or... use mode and prescale to set stuff up
	TCCR1B = (1<<ICNC1) | (mode & MSCmEDGE) | (prescale);
}

// invert the trigger edge
static inline void capture_edge_invert(void)
{
	TCCR1B ^= (1<<ICES1);
}

// read the time register as one full integer value
//  C magically does the right two byte read sequence
static inline uint16_t capture_read(void)
{
	return ICR1;
}

// check if the timer overflow interrupt bit
//  has been set (but not yet handled)
static inline uint8_t capture_overflow(void)
{
	return TIFR1 & (1<<TOV1);
}

// reset that darned overflow bit
static inline uint8_t capture_overflow_reset(void)
{
	return TIFR1 = (1<<TOV1);
}

// backout capture lib Timer1 register settings 
// and disable interrupts in TIMSK1
static inline void capture_shutdown(void)
{
	TCCR1B = 0;
	TIMSK1 = 0;
	TCCR1A = saveTCCR1A;
	TCCR1B = saveTCCR1B;
}


#define TIMER_OVERFLOW_VECTOR  TIMER1_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER1_CAPT_vect 


#elif defined(CAPTURE_USE_TIMER3)

static uint8_t saveTCCR3A, saveTCCR3B;

static inline void capture_init(void)
{
	saveTCCR3A = TCCR3A;
	saveTCCR3B = TCCR3B;
	TCCR3B = 0;
	TCCR3A = 0;
	TCNT3 = 0;
	TIFR3 = (1<<ICF3) | (1<<TOV3);
	TIMSK3 = (1<<ICIE3) | (1<<TOIE3);
}

// scale and mode not implemented
static inline void capture_start( uint8_t prescale, uint8_t mode )
{
	TCCR3B = (1<<ICNC3) | (1<<ICES3) | (1<<CS30);
}

static inline uint16_t capture_read(void)
{
	return ICR3;
}

static inline uint8_t capture_overflow(void)
{
	return TIFR3 & (1<<TOV3);
}

static inline uint8_t capture_overflow_reset(void)
{
	return TIFR3 = (1<<TOV3);
}

static inline void capture_shutdown(void)
{
	TCCR3B = 0;
	TIMSK3 = 0;
	TCCR3A = saveTCCR3A;
	TCCR3B = saveTCCR3B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER3_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER3_CAPT_vect 



#elif defined(CAPTURE_USE_TIMER4)

static uint8_t saveTCCR4A, saveTCCR4B;

static inline void capture_init(void)
{
	saveTCCR4A = TCCR4A;
	saveTCCR4B = TCCR4B;
	TCCR4B = 0;
	TCCR4A = 0;
	TCNT4 = 0;
	TIFR4 = (1<<ICF4) | (1<<TOV4);
	TIMSK4 = (1<<ICIE4) | (1<<TOIE4);
}

// scale and mode not implemented
static inline void capture_start( uint8_t prescale, uint8_t mode )
{
	TCCR4B = (1<<ICNC4) | (1<<ICES4) | (1<<CS40);
}

static inline uint16_t capture_read(void)
{
	return ICR4;
}

static inline uint8_t capture_overflow(void)
{
	return TIFR4 & (1<<TOV4);
}

static inline uint8_t capture_overflow_reset(void)
{
	return TIFR4 = (1<<TOV4);
}

static inline void capture_shutdown(void)
{
	TCCR4B = 0;
	TIMSK4 = 0;
	TCCR4A = saveTCCR4A;
	TCCR4B = saveTCCR4B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER4_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER4_CAPT_vect 



#elif defined(CAPTURE_USE_TIMER5)

static uint8_t saveTCCR5A, saveTCCR5B;

static inline void capture_init(void)
{
	saveTCCR5A = TCCR5A;
	saveTCCR5B = TCCR5B;
	TCCR5B = 0;
	TCCR5A = 0;
	TCNT5 = 0;
	TIFR5 = (1<<ICF5) | (1<<TOV5);
	TIMSK5 = (1<<ICIE5) | (1<<TOIE5);
}

// scale and mode not implemented
static inline void capture_start( uint8_t prescale, uint8_t mode )
{
	TCCR5B = (1<<ICNC5) | (1<<ICES5) | (1<<CS50);
}

static inline uint16_t capture_read(void)
{
	return ICR5;
}

static inline uint8_t capture_overflow(void)
{
	return TIFR5 & (1<<TOV5);
}

static inline uint8_t capture_overflow_reset(void)
{
	return TIFR5 = (1<<TOV5);
}

static inline void capture_shutdown(void)
{
	TCCR5B = 0;
	TIMSK5 = 0;
	TCCR5A = saveTCCR5A;
	TCCR5B = saveTCCR5B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER5_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER5_CAPT_vect 




#endif // capture_H


#endif
