/* MSCapture Library
 *    For measuring relatively low frequencies and
 *    pulse widths using Timer Capture function.
 *    For the UNO and ProMini the only input is D8 and
 *    timer1 functions -- Servo library and D9,10 PWM -- are not usable.
 * 	  Other chips are not yet supported.
 *
 * 	  Timer resolution and counting mode are set by the call to
 *
 * 	   MSCapture.begin( prescale, mode )
 *
 * 	  where:
 * 	    	prescale -- sets the internal clock frequency, measure resolution
 *		MSCck1	 	= 0x01,	// 16Mhz - 0.0625us  (max 272sec, 4.5min)
 *		MSCck8	 	= 0x02,	//  2Mhz - 0.5uS     (max 2147sec, 35min)
 *		MSCck64		= 0x03,	// 250Khz - 4uS      (max 286m, 4.7hr)
 *		MSCck256	= 0x04,	//  62.5KHz - 16uS   (max 19hr)
 *		MSCck1024	= 0x05,	//  15.625Khz - 64uS (max 76.3hr)
 *		
 *		    mode -- sets the measure counting style,
 *		MSCmPERIOD 	= 0xC0,	// pulse period, btw rising edges
 *		MSCmHPULSE 	= 0x40,	// HIGH pulse length
 *		MSCmLPULSE	= 0x80,	// LOW pulse length
 *		MSCmAPULSE	= 0x00,	// alternating H/L pulse lengths, starts w/LOW
 *
 * 	  A small number of Results are buffered interally.
 * 	  A call to
 * 	   
 * 	   uint8_t num = MSCapture.available()
 *
 * 	  will return the number of results available, and
 * 	  A call to
 *
 * 	   uint32_t count = MSCapture.read()
 *
 * 	  will return the next available value, removing it from the buffer.
 *
 *  based on:
 * http://www.pjrc.com/teensy/td_libs_FrequencyMeasure.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
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "./MSCapture.h"
#include "./capture.h"

// convenient way to make two words into one long w/o all that shiftlessness
typedef union
{
	uint32_t longw;
	struct
	{
		uint16_t low;
		uint16_t high;
	};
} Lword;

// maintain a circular buffer of results in case we get away with ourselves
// if you run away, make it bigger in MSCapture.h...
//#define MSCAPTURE_BUFFER_LEN 4
static volatile uint32_t buffer_value[MSCAPTURE_BUFFER_LEN];
static volatile uint8_t buffer_head;
static volatile uint8_t buffer_tail;
// timer wrap-around counter, adds top 16 bits to result counts
static uint16_t capture_msw;
// last count value received, including MSW
//static uint32_t capture_previous;
// last count value received, NOT including MSW because we 0 it on each pass
static uint16_t capture_previous;
// operating states
static uint8_t state;	// bit mapped as below
enum
{
	MSCinit	= 0x00,		// initializing
	MSCrun	= 0x01,		// setup and running correctly
	MSCcnt	= 0x02,		// actually counting

	// note1: MSCrun is probably redundant at this juncture...
	// note2: top two bits 6,7 are saved MSCmMODE mode and edge settings
} MSCSTATE;


/** get everything started up and counting
 *   todo: add arguments to set capture mode and timer prescale 
 **/
void MSCaptureClass::begin( uint8_t prescale, uint8_t mode )
{
	capture_init();
	capture_msw = 0;
	capture_previous = 0;
	buffer_head = 0;
	buffer_tail = 0;
	state = MSCinit | (mode & MSCmMODE);
	capture_start( prescale, mode );
}

/** return the number of counted events available in the buffer
 *   Note: I think it is OK to not shut off interrupts during
 *    available() and read(). If buffer_head is incremented
 *    while these functions are executing we will, at worst,
 *    come up empty on the current call.
 **/
uint8_t MSCaptureClass::available(void)
{
	uint8_t head, tail;

	head = buffer_head;
	tail = buffer_tail;
	if (head >= tail) return head - tail;
	return MSCAPTURE_BUFFER_LEN + head - tail;
}

/** return the next available capture count from the buffer
 **   removes the count from the buffer and frees slot for new data
 ** if nothing in the buffer return MAX -- 0xFFFFFFFF
 **/
uint32_t MSCaptureClass::read(void)
{
	uint8_t head, tail;
	uint32_t value;

	head = buffer_head;
	tail = buffer_tail;
	if (head == tail) return 0xFFFFFFFF;
	tail = tail + 1;
	if (tail >= MSCAPTURE_BUFFER_LEN) tail = 0;
	value = buffer_value[tail];
	buffer_tail = tail;
	return value;
}

/** shut it down when we don't want it anymore
 **/
void MSCaptureClass::end(void)
{
	capture_shutdown();
}


// Timer Overflow interrupt,
//  increments high word count to extend count range
// clamp at maximum value in case we really roll over
//  this will force the first returned count to be very large
ISR(TIMER_OVERFLOW_VECTOR)
{
	if( ++capture_msw == 0 )
		--capture_msw;	// reset to 0xffff

}

// Capture Event interrupt
//  do stuff and save results
ISR(TIMER_CAPTURE_VECTOR)
{
	uint16_t capture_lsw;
//	uint32_t capture;
	Lword capture;
	uint32_t period;
	uint8_t i;

	// get the captured timer value
	capture_lsw = capture_read();

	// Handle the case where but capture and overflow interrupts were pending
	// (eg, int were disabled for a while), or where the overflow occurred
	// while this ISR was starting up.  However, if we read a 16 bit number
	// that is very close to overflow, then ignore any overflow since it
	// probably just happened.
	if (capture_overflow() && capture_lsw < 0xFF00)
	{
		capture_overflow_reset();
		if( ++capture_msw == 0 )	// incr and deal with wrap-around
			--capture_msw;	// reset to 0xffff
	}

	// compute the actual period
	//  add MSW overflow counts to time capture value
	//  subtract the previously captured value
	//  and save the new one for next time
	// Note: the original doesn't handle initial startup value correctly
	//   (ignore first result)
	//   nor 32bit MSW overflow which will happen every 260sec at fastest
	//   (clear MSW each time)
	//	Note: instead of shifting and adding use the magic of a union
	//	  capture = ((uint32_t)capture_msw << 16) | capture_lsw;
	//	to combine the two independent counts LSW and MSW into a long word
	capture.low = capture_lsw;
	capture.high = capture_msw;
	period = capture.longw - capture_previous;
	// instead of calculating with the whole result
	//	capture_previous = capture.longw;
	// just use the counter LSW portion and 0 the MSW
	// this also (maybe) fixes the possible MSW wraparound issue
	// but has to happen within the full counter cycle, 4ms at fastest...
	capture_previous = capture.low;
	capture_msw = 0;

	// don't save the first sample (not running and/or not counting)
	//  because it's un-synced in PERIOD mode and/or
	//  the beginning of counting in PULSE mode
	if( (state & (MSCrun | MSCcnt)) != (MSCrun | MSCcnt) )
	{ 
		// go to running and counting state
		state |= (MSCrun | MSCcnt);
		// if not in PERIOD mode, invert the trigger edge bit
		//  so we finish counting on the opposite PULSE edge
		if( (state & MSCmMODE) != MSCmPERIOD )
			capture_edge_invert();

		return;
	}

	// got a counted result...
	// store this period in the buffer,
	//  unless we have overrun the buffer, then silently drop it
	i = buffer_head + 1;
	if (i >= MSCAPTURE_BUFFER_LEN)	i = 0;
	if (i != buffer_tail)
	{
		buffer_value[i] = period;
		buffer_head = i;
	}

	// implement pulse width capture by switching edge sensitivity
	//  alternate set/clear of ICES1 (capture edge select)) in TCCR1B
	// if in PERIOD mode, just keep doing what we are doing
	if( (state & MSCmMODE) == MSCmPERIOD )
		return;

	// otherwise we just finished a PULSE width measurement
	//  invert the trigger edge bit and go to not-counting state
	// unless we want to measure swinging both ways,
	//  then just stay counting...
	capture_edge_invert();
	if( (state & MSCmMODE) != MSCmAPULSE )
		state ^= MSCcnt;

	return;
}

/** Only one of these allowed in system
 *   so we declare it here for users to abuse...
 **/
MSCaptureClass MSCapture;

