/* 
* classPio.cpp
*
* Created: 01/02/2020 20:24:27
* Author: Ledo
*/


#include "classPio.h"

// default constructor
classPio::classPio()
{
} //classPio

// default destructor
classPio::~classPio()
{
} //~classPio

void classPio::PeripheralClock_enable(Pio *p_pio)
{
	int32_t pioId;
	if(p_pio == PIOA)
	 pioId = ID_PIOA;
	 else if(p_pio == PIOB)
	  pioId = ID_PIOB;
	 else if(p_pio == PIOC)
	  pioId = ID_PIOC;
	 else if(p_pio == PIOD)
	  pioId = ID_PIOD;
	 
	PMC->PMC_WPMR = (PMC_WPKEY << 8) | 0x0;//Write prot disable
	PMC->PMC_PCER0 = (1 << pioId);
}

/**
 * \brief Configure one or more pin(s) or a PIO controller as inputs.
 * Optionally, the corresponding internal pull-up(s) and glitch filter(s) can
 * be enabled.
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Bitmask indicating which pin(s) to configure as input(s).
 * \param ul_attribute PIO attribute(s).
 */
void classPio::pin_set_input(Pio *p_pio, const uint32_t ul_mask,
		                     const uint32_t ul_attribute)
{
	//pio_disable_interrupt(p_pio, ul_mask);
	p_pio->PIO_IDR = ul_mask;
	
	/* Enable the pull-up(s) if necessary */
	if (ul_attribute & PIO_PULLUP) 
		p_pio->PIO_PUER = ul_mask;
			else 
			    p_pio->PIO_PUDR = ul_mask;

	/* Enable Input Filter if necessary */
	if (ul_attribute & (PIO_DEGLITCH | PIO_DEBOUNCE))
		p_pio->PIO_IFER = ul_mask;
	     else 
		     p_pio->PIO_IFDR = ul_mask;

	/* Enable de-glitch or de-bounce if necessary */
	if (ul_attribute & PIO_DEGLITCH) 
		p_pio->PIO_IFSCDR = ul_mask;
	    else 
		  if (ul_attribute & PIO_DEBOUNCE) 
			p_pio->PIO_IFSCER = ul_mask;

	/* Configure pin as input */
	p_pio->PIO_ODR = ul_mask;
	p_pio->PIO_PER = ul_mask;
}

/**
 * \brief Configure one or more pin(s) of a PIO controller as outputs, with
 * the given default value. Optionally, the multi-drive feature can be enabled
 * on the pin(s).
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Bitmask indicating which pin(s) to configure.
 * \param ul_default_level Default level on the pin(s).
 * \param ul_multidrive_enable Indicates if the pin(s) shall be configured as
 * open-drain.
 * \param ul_pull_up_enable Indicates if the pin shall have its pull-up
 * activated.
 */
void classPio::pin_set_output(Pio *p_pio, const uint32_t ul_mask,
		                      const uint32_t ul_default_level,
		                      const uint32_t ul_multidrive_enable,
		                      const uint32_t ul_pull_up_enable)
{
	//pio_disable_interrupt(p_pio, ul_mask);
	p_pio->PIO_IDR = ul_mask;
	
	/* Enable the pull-up(s) if necessary */
	if (ul_pull_up_enable)
	  p_pio->PIO_PUER = ul_mask;
	   else
	      p_pio->PIO_PUDR = ul_mask;

	/* Enable multi-drive if necessary */
	if (ul_multidrive_enable) 
		p_pio->PIO_MDER = ul_mask;
	 else 
		p_pio->PIO_MDDR = ul_mask;
	

	/* Set default value */
	if (ul_default_level) 
		p_pio->PIO_SODR = ul_mask;
	 else 
		p_pio->PIO_CODR = ul_mask;
	

	/* Configure pin(s) as output(s) */
	p_pio->PIO_OER = ul_mask;
	p_pio->PIO_PER = ul_mask;
}

/**
 * \brief Configure Glitch or Debouncing filter for the specified input(s).
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Bitmask of one or more pin(s) to configure.
 * \param ul_cut_off Cuts off frequency for debouncing filter.
 */
void classPio::pin_set_debounce_filter(Pio *p_pio, const uint32_t ul_mask,
		const uint32_t ul_cut_off)
{
	/* Set Debouncing, 0 bit field no effect */
	p_pio->PIO_IFSCER = ul_mask;

	/*
	 * The debouncing filter can filter a pulse of less than 1/2 Period of a
	 * programmable Divided Slow Clock:
	 * Tdiv_slclk = ((DIV+1)*2).Tslow_clock
	 */
	p_pio->PIO_SCDR = PIO_SCDR_DIV((32768 /
			(2 * (ul_cut_off))) - 1);
}


/**
 * \brief Configure IO of a PIO controller as being controlled by a specific
 * peripheral.
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_type PIO type.
 * \param ul_mask Bitmask of one or more pin(s) to configure.
 */
void classPio::pin_set_peripheral(Pio *p_pio, const pio_type_t ul_type,
		const uint32_t ul_mask)
{
	uint32_t ul_sr;

	/* Disable interrupts on the pin(s) */
	p_pio->PIO_IDR = ul_mask;

	switch (ul_type) {
	case PIO_PERIPH_A:
		ul_sr = p_pio->PIO_ABCDSR[0];
		p_pio->PIO_ABCDSR[0] &= (~ul_mask & ul_sr);

		ul_sr = p_pio->PIO_ABCDSR[1];
		p_pio->PIO_ABCDSR[1] &= (~ul_mask & ul_sr);
		break;
	case PIO_PERIPH_B:
		ul_sr = p_pio->PIO_ABCDSR[0];
		p_pio->PIO_ABCDSR[0] = (ul_mask | ul_sr);

		ul_sr = p_pio->PIO_ABCDSR[1];
		p_pio->PIO_ABCDSR[1] &= (~ul_mask & ul_sr);
		break;
	case PIO_PERIPH_C:
		ul_sr = p_pio->PIO_ABCDSR[0];
		p_pio->PIO_ABCDSR[0] &= (~ul_mask & ul_sr);

		ul_sr = p_pio->PIO_ABCDSR[1];
		p_pio->PIO_ABCDSR[1] = (ul_mask | ul_sr);
		break;
	case PIO_PERIPH_D:
		ul_sr = p_pio->PIO_ABCDSR[0];
		p_pio->PIO_ABCDSR[0] = (ul_mask | ul_sr);

		ul_sr = p_pio->PIO_ABCDSR[1];
		p_pio->PIO_ABCDSR[1] = (ul_mask | ul_sr);
		break;
		/* Other types are invalid in this function */
	case PIO_INPUT:
	case PIO_OUTPUT_0:
	case PIO_OUTPUT_1:
	case PIO_NOT_A_PIN:
		return;
	}

	/* Remove the pins from under the control of PIO */
	p_pio->PIO_PDR = ul_mask;
}


/**
 * \brief Drive a GPIO pin to 1.
 *
 * \param ul_pin The pin index.
 *
 * \note The function \ref pio_configure_pin must be called beforehand.
 */
void classPio::set_pin_high(Pio *p_pio, uint32_t ul_pin)
{

	/* Value to be driven on the I/O line: 1. */
	p_pio->PIO_SODR = 1 << (ul_pin & 0x1F);
}

/**
 * \brief Drive a GPIO pin to 0.
 *
 * \param ul_pin The pin index.
 *
 * \note The function \ref pio_configure_pin must be called before.
 */
void classPio::set_pin_low(Pio *p_pio, uint32_t ul_pin)
{
	/* Value to be driven on the I/O line: 0. */
	p_pio->PIO_CODR = 1 << (ul_pin & 0x1F);
}

/**
 * \brief Toggle a GPIO pin.
 *
 * \param ul_pin The pin index.
 *
 * \note The function \ref pio_configure_pin must be called before.
 */
void classPio::toggle_pin(Pio *p_pio, uint32_t ul_pin)
{

	if (p_pio->PIO_ODSR & (1 << (ul_pin & 0x1F))) {
		/* Value to be driven on the I/O line: 0. */
		p_pio->PIO_CODR = 1 << (ul_pin & 0x1F);
	} else {
		/* Value to be driven on the I/O line: 1. */
		p_pio->PIO_SODR = 1 << (ul_pin & 0x1F);
	}
}

//Return pin state
bool classPio::get_pin_level(Pio *p_pio, uint32_t ul_pin)
{
	return p_pio->PIO_PDSR & (1 << ul_pin) ;
}

/**
 * \brief Enable interrupt for a GPIO pin.
 *
 * \param ul_pin The pin index.
 *
 * \note The function \ref gpio_configure_pin must be called before.
 */
void classPio::enable_pin_interrupt(Pio *p_pio, uint32_t ul_pin)
{
	p_pio->PIO_IER = 1 << (ul_pin & 0x1F);
}


/**
 * \brief Disable interrupt for a GPIO pin.
 *
 * \param ul_pin The pin index.
 *
 * \note The function \ref gpio_configure_pin must be called before.
 */
void classPio::disable_pin_interrupt(Pio *p_pio, uint32_t ul_pin)
{
	p_pio->PIO_IDR = 1 << (ul_pin & 0x1F);
}


/**
 * \brief Process an interrupt request on the given PIO controller.
 *
 * \param p_pio PIO controller base address.
 * \param ul_id PIO controller ID.
 */
void classPio::handler_process(Pio *p_pio, uint32_t ul_id)
{
	uint32_t status;
	uint32_t i;

	/* Read PIO controller status */
	status = p_pio->PIO_ISR;
	status &= p_pio->PIO_IMR;
	/* Check pending events */
	if (status != 0) {
		/* Find triggering source */
		i = 0;
		while (status != 0) {
			/* Source is configured on the same controller */
			if (gs_interrupt_sources[i].id == ul_id) {
				/* Source has PIOs whose statuses have changed */
				if ((status & gs_interrupt_sources[i].mask) != 0) {
					gs_interrupt_sources[i].handler(gs_interrupt_sources[i].id,
							gs_interrupt_sources[i].mask);
					status &= ~(gs_interrupt_sources[i].mask);
				}
			}
			i++;
			if (i >= MAX_INTERRUPT_SOURCES) {
				break;
			}
		}
	}

	/* Check capture events */
	if (pio_capture_enable_flag) {
		if (pio_capture_handler) {
			pio_capture_handler(p_pio);
		}
	}
}

/**
 * \brief Set an interrupt handler for the provided pins.
 * The provided handler will be called with the triggering pin as its parameter
 * as soon as an interrupt is detected.
 *
 * \param p_pio PIO controller base address.
 * \param ul_id PIO ID.
 * \param ul_mask Pins (bit mask) to configure.
 * \param ul_attr Pins attribute to configure.
 * \param p_handler Interrupt handler function pointer.
 *
 * \return 0 if successful, 1 if the maximum number of sources has been defined.
 */
uint32_t classPio::handler_set(Pio *p_pio, uint32_t ul_id, uint32_t ul_mask,
		uint32_t ul_attr, void (*p_handler) (uint32_t, uint32_t))
{
    uint8_t i;
	struct s_interrupt_source *pSource;

	if (gs_ul_nb_sources >= MAX_INTERRUPT_SOURCES)
		return 1;

    /* Check interrupt for this pin, if already defined, redefine it. */
	for (i = 0; i <= gs_ul_nb_sources; i++) {
		pSource = &(gs_interrupt_sources[i]);
		if (pSource->id == ul_id && pSource->mask == ul_mask) {
			break;
		}
	}

	/* Define new source */
	pSource->id = ul_id;
	pSource->mask = ul_mask;
	pSource->attr = ul_attr;
	pSource->handler = p_handler;
	if (i == gs_ul_nb_sources + 1) {
		gs_ul_nb_sources++;
	}

	/* Configure interrupt mode */
	configure_interrupt(p_pio, ul_mask, ul_attr);

	return 0;
}

/**
 * \brief Configure the given interrupt source.
 * Interrupt can be configured to trigger on rising edge, falling edge,
 * high level, low level or simply on level change.
 *
 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Interrupt source bit map.
 * \param ul_attr Interrupt source attributes.
 */
void classPio::configure_interrupt(Pio *p_pio, const uint32_t ul_mask,
		const uint32_t ul_attr)
{
	/* Configure additional interrupt mode registers. */
	if (ul_attr & PIO_IT_AIME) {
		/* Enable additional interrupt mode. */
		p_pio->PIO_AIMER = ul_mask;

		/* If bit field of the selected pin is 1, set as
		   Rising Edge/High level detection event. */
		if (ul_attr & PIO_IT_RE_OR_HL) {
			/* Rising Edge or High Level */
			p_pio->PIO_REHLSR = ul_mask;
		} else {
			/* Falling Edge or Low Level */
			p_pio->PIO_FELLSR = ul_mask;
		}

		/* If bit field of the selected pin is 1, set as
		   edge detection source. */
		if (ul_attr & PIO_IT_EDGE) {
			/* Edge select */
			p_pio->PIO_ESR = ul_mask;
		} else {
			/* Level select */
			p_pio->PIO_LSR = ul_mask;
		}
	} else {
		/* Disable additional interrupt mode. */
		p_pio->PIO_AIMDR = ul_mask;
	}
}

/**
 * \brief Set a capture interrupt handler for all PIO.
 *
 * The handler will be called with the triggering PIO as its parameter
 * as soon as an interrupt is detected.
 *
 * \param p_handler Interrupt handler function pointer.
 *
 */
void classPio::capture_handler_set(void (*p_handler)(Pio *))
{
	pio_capture_handler = p_handler;
}

/**
 * \brief Set an interrupt handler for the specified pin.
 * The provided handler will be called with the triggering pin as its parameter
 * as soon as an interrupt is detected.
 *
 * \param ul_pin Pin index to configure.
 * \param ul_flag Pin flag.
 * \param p_handler Interrupt handler function pointer.
 *
 * \return 0 if successful, 1 if the maximum number of sources has been defined.
 */
uint32_t classPio::handler_set_pin(Pio *p_pio, uint32_t ul_pin, uint32_t ul_flag,
		void (*p_handler) (uint32_t, uint32_t))
{

	return handler_set(p_pio, 4, 4, ul_flag, p_handler);//revisar
	//return handler_set(p_pio, group_id, group_mask, ul_flag, p_handler);
}

/**
 * \brief Parallel IO Controller A interrupt handler.
 * Redefined PIOA interrupt handler for NVIC interrupt table.
 */
void classPio::PIOA_Handler(void)
{
	handler_process(PIOA, ID_PIOA);
}

/**
 * \brief Parallel IO Controller B interrupt handler
 * Redefined PIOB interrupt handler for NVIC interrupt table.
 */
void classPio::PIOB_Handler(void)
{
    handler_process(PIOB, ID_PIOB);
}



/**
 * \brief Parallel IO Controller D interrupt handler.
 * Redefined PIOD interrupt handler for NVIC interrupt table.
 */
void classPio::PIOD_Handler(void)
{
	handler_process(PIOD, ID_PIOD);
}


/**
 * \brief Initialize PIO interrupt management logic.
 *
 * \param p_pio PIO controller base address.
 * \param ul_irqn NVIC line number.
 * \param ul_priority PIO controller interrupts priority.
 */
void classPio::NVIC_handler_register(Pio *p_pio, IRQn_Type ul_irqn, uint32_t ul_priority)
{
	uint32_t bitmask = 0;
    uint32_t dummy;
	bitmask = p_pio->PIO_IMR;
	p_pio->PIO_IDR = 0xFFFFFFFF;//disable interrupts
	dummy = p_pio->PIO_ISR;//??
	NVIC_DisableIRQ(ul_irqn);
	NVIC_ClearPendingIRQ(ul_irqn);
	NVIC_SetPriority(ul_irqn, ul_priority);
	NVIC_EnableIRQ(ul_irqn);
	p_pio->PIO_IER = bitmask;//enable interrupts
}








