//
// AVR IO pin operation
// Homelab library
//
// Department of Mechatronics
// Tallinn University of Technology
// © Copyrights 2009
//

//
// Include pin.h only once
//
#ifndef _PIN_H_
#define _PIN_H_

//
// Include common library
//
#include "common.h"
#include "delay.h"

//
// Include avrlibc files
//
#include <avr/io.h>

//
// Take use of bit manipulation operations
//
#include "bit.h"

//
// C++ compatibility
//
#ifdef __cplusplus
extern "C" {
#endif

//
// Structure for holding registry addresses and bitmask
//
#if defined (__AVR_ATmega2561__) || defined (__AVR_ATmega128__)
	typedef struct pin
	{
		_REG_PTR_ ddr;
		_REG_PTR_ port;
		_REG_PTR_ pin;
		uint8_t mask;
	}
	pin;

	//
	// Macro for defining a pin
	//
	#define PIN(port_char, bit_index) \
	{ \
		(_REG_PTR_)&DDR ## port_char, \
		(_REG_PTR_)&PORT ## port_char, \
		(_REG_PTR_)&PIN ## port_char, \
		bit_mask(bit_index) \
	}
	
	//
	// Function for configuring pin as an input with pull-up resistor
	//
	inline void pin_setup_input_with_pullup(pin pin)
	{
		bitmask_clear(*pin.ddr, pin.mask);
		bitmask_set(*pin.port, pin.mask);
	}
	
	//
	// Function for configuring pin as an high-z (tri-state) input
	//
	inline void pin_setup_input(pin pin)
	{
		bitmask_clear(*pin.ddr, pin.mask);
	}
#elif defined (__AVR_ATxmega128A1U__)
	typedef struct pin
	{
		_REG_PTR_ ddr;
		_REG_PTR_ port;
		_REG_PTR_ pin;
		_REG_PTR_ cfg;
		uint8_t mask;
	}
	pin;

	//
	// Macro for defining a pin
	//
	#define PIN(port_char, bit_index) \
	{ \
		(_REG_PTR_)&PORT ## port_char ## _DIR, \
		(_REG_PTR_)&PORT ## port_char ## _OUT, \
		(_REG_PTR_)&PORT ## port_char ## _IN, \
		(_REG_PTR_)&PORT ## port_char ## _PIN ## bit_index ## CTRL, \
		bit_mask(bit_index) \
	}
	
	//
	// Function for configuring pin as an input with pull-up resistor
	//
	inline void pin_setup_input_with_pullup(pin pin)
	{
		bitmask_clear(*pin.ddr, pin.mask);
		bitmask_set(*pin.cfg, (3<<3));
	}
	
	//
	// Function for configuring pin as an high-z (tri-state) input
	//
	inline void pin_setup_input(pin pin)
	{
		bitmask_clear(*pin.ddr, pin.mask);
		bitmask_clear(*pin.cfg, (3<<3));
	}
#else
	#error "Not Homelab compatible part"
#endif

//
// Define default LED and button pins
//
extern pin led_debug;
extern pin led_red;
extern pin led_yellow;
extern pin led_green;
extern pin LED0;
extern pin LED1;
extern pin LED2;
extern pin LED3;

extern pin S0;
extern pin S1;
extern pin S2;
extern pin S3;

//
// Function for configuring pin as an output
//
inline void pin_setup_output(pin pin)
{
	bitmask_set(*pin.ddr, pin.mask);
}

//
// Function for setting pin output high
//
inline void pin_set(pin pin)
{
	bitmask_set(*pin.port, pin.mask);
}

//
// Function for setting pin output low
//
inline void pin_clear(pin pin)
{
	bitmask_clear(*pin.port, pin.mask);
}

//
// Function for toggling pin output
//
inline void pin_toggle(pin pin)
{
	bitmask_invert(*pin.port, pin.mask);
}

//
// Function for setting pin output to specified value
//
inline void pin_set_to(pin pin, bool value)
{
	bitmask_set_to(*pin.port, pin.mask, value);
}

//
// Function for getting pin value
//
inline bool pin_get_value(pin pin)
{
	return (bitmask_is_set(*pin.pin, pin.mask) ? true : false);
}

//
// Function for filtering bounces on pin connected to the button
//
extern bool pin_get_debounced_value_proto(pin button, delay_function_ptr ms_delay_function);
#define pin_get_debounced_value(button) pin_get_debounced_value_proto(button, &sw_delay_ms)

//
// Turn LED on
//
inline void led_off (pin pin)
{
	pin_set(pin);
}

//
// Turn LED off
//
inline void led_on (pin pin)
{
	pin_clear(pin);
}
//
// Read Button state
// return 1 - button pressed
// return 0 - button unpressed
//
bool button_read(pin button);

//
// Helper function for tracking compiled code
//
void supervisor_check(unsigned int repeat);

//
// Special helper functions
//
#define PIN_ARRAY_OF_2_MASK(array, value) \
	pin_set_to(array[0], (value) & 0x01); \
	pin_set_to(array[1], (value) & 0x02);

#define PIN_ARRAY_OF_3_MASK(array, value) \
	pin_set_to(array[0], (value) & 0x01); \
	pin_set_to(array[1], (value) & 0x02); \
	pin_set_to(array[2], (value) & 0x04);

#define PIN_ARRAY_OF_4_MASK(array, value) \
	pin_set_to(array[0], (value) & 0x01); \
	pin_set_to(array[1], (value) & 0x02); \
	pin_set_to(array[2], (value) & 0x04); \
	pin_set_to(array[3], (value) & 0x08);

//
// C++ compatibility
//
#ifdef __cplusplus
}
#endif

#endif
