/**
  **************************************************************************************************
  * @file		pin.c
  * @author		Kerem Yollu & Edwin Koch
  * @date		03.11.2021
  * @version	1.01
  ************************************************************************************************** 
  * @brief		Implementation of pin.h for the STM32F042K6 MCU
  * 
  * **Detailed Description :**
  * 
  * This source code uses bit manipulation in order to minimise the footprint of pin initialisation 
  * and manipulation. It's based on the CMSIS/Device/ST/STM32F0xx/Include/stm32f042x6.h Header file
  * to get the obtain the right Registers. 
  *
  *
  * @todo 
  * - 01.11.2021 : Should we add a seprate header in the cls layer containing the pinNo_t ?
  *	- 01.11.2021 : Depending on request implment a pinLock() function
  **************************************************************************************************
*/

#include "pin.h"

#define MODER_IN		0x0UL	
#define MODER_OUT		0x1UL		
#define MODER_ALTERNATE	0x2UL		
#define MODER_ANALOG	0x3UL	

#define OSPEEDR_LOW		0x0UL
#define OSPEEDR_MEDIUM	0x1UL
#define OSPEEDR_HIGH	0x3UL

#define PUPDR_NO_PULL	0x0UL
#define PUPDR_PULL_UP	0x1UL
#define PUPDR_PULL_DOWN	0x2UL

#define OTYPER_PUSH_PULL	0x0UL
#define OTYPER_OPEN_DRAIN	0x1UL

#define PIN_NO		(pinNo & 0x0F)
#define PIN_PORT	((pinNo & 0xF0)>>4)
#define PIN_BASE	((GPIO_TypeDef *)portBase_Addr_List[PIN_PORT])

/*! Table for binding the Pin mode_t enum index to the values that the MODER register needs */
const uint32_t moderMode[5] = {
	MODER_ANALOG, 
	MODER_IN,
	MODER_OUT, 
	MODER_ANALOG,
	MODER_ALTERNATE
};

/**
* Table for binding the Pin pinNo_t's Port Offet to the PORT gesiter that needs to be activated
*/
const uint32_t pinPort[3] = {
	RCC_AHBENR_GPIOAEN, 
	RCC_AHBENR_GPIOBEN, 
	RCC_AHBENR_GPIOFEN 
};

/**
* Table for binding the Pin pullUpDown_t enum index to the values that the PUPDR register needs
*/
const uint32_t pinPullUpDown[4] = {
	PUPDR_NO_PULL,
	PUPDR_NO_PULL,
	PUPDR_PULL_UP,
	PUPDR_PULL_DOWN
};

/**
* Table for binding the Pin speed_t enum index to the values that the OSPEEDR register needs
*/
const uint32_t speedList[5] = {
	OSPEEDR_MEDIUM,
	OSPEEDR_LOW,
	OSPEEDR_LOW,
	OSPEEDR_MEDIUM,
	OSPEEDR_HIGH
};


/**
* Table for binding the Pin stage_t enum index to the values that the OTYPER register needs
*/
const uint32_t outputStgeList[4] = 
{
	OTYPER_PUSH_PULL,
	OTYPER_OPEN_DRAIN,
	OTYPER_PUSH_PULL,
	OTYPER_OPEN_DRAIN
};

void pinSetMode(pinNo_t pinNo, pinMode_t mode)
{
	//Clear entry.
	PIN_BASE->MODER &=~ (0x3 << (PIN_NO * 2));
	//Set to corresponding mode.
	PIN_BASE->MODER |= (moderMode[mode] << (PIN_NO * 2));
}

void pinSetOutputStage(pinNo_t pinNo, pinStage_t stage)
{
	PIN_BASE->OTYPER &= ~(1 << PIN_NO);	
	PIN_BASE->OTYPER |= (outputStgeList[stage] << PIN_NO);
}
 
void pinSetPullUpDonw(pinNo_t pinNo, pinPullUpDown_t resistance)
{
    PIN_BASE->PUPDR &= ~(0x3 << (PIN_NO * 2));
    PIN_BASE->PUPDR |= (pinPullUpDown[resistance] << (PIN_NO * 2));
}
 
void pinSetSpeed(pinNo_t pinNo, pinSpeed_t speed)
{
	PIN_BASE->OSPEEDR &= (0x3 << (PIN_NO * 2));	
	PIN_BASE->OSPEEDR |= (speedList[speed] << (PIN_NO * 2));
}
 
void pinSetAlternate(pinNo_t pinNo, uint16_t alternate)
{
	if(!(altFunc_List[PIN_PORT][PIN_NO] & (1<<(7-alternate))))	
	{
		pinThrowError(UnvalidAlternate);	
	}
	if(alternate > 15)
	{
		pinThrowError(OutOfRangeAlternate);	
	}

	if(PIN_NO < 8) {
		PIN_BASE->AFR[0] &= ~(0x0F << (PIN_NO * 4));
		PIN_BASE->AFR[0] |= ((alternate & 0x0F) << (PIN_NO * 4));
		return; 
	}

	PIN_BASE->AFR[1] &= ~(0x0F << ((PIN_NO-8) * 4));
	PIN_BASE->AFR[1] |= ((alternate & 0x0F) << ((PIN_NO-8) * 4));
}

void pinConfig(pinNo_t pinNo, pinMode_t mode, pinStage_t stage, pinPullUpDown_t resistance, pinSpeed_t speed)
{
	pinInit(pinNo); //Very important to init first so that the corresponding bus gets his clock	
	pinSetMode(pinNo, mode); 	
	pinSetOutputStage(pinNo, stage); 
	pinSetPullUpDonw(pinNo, resistance);
	pinSetSpeed(pinNo,speed); 
}

uint8_t pinRead(pinNo_t pinNo)
{
	return ((PIN_BASE->IDR & (1<<PIN_NO)) >> PIN_NO);
}

void pinToggle(pinNo_t pinNo)
{
	if(pinRead(pinNo))
	{
		PIN_BASE->BSRR |= (GPIO_BSRR_BR_0 << (PIN_NO));
		return;
	}
		PIN_BASE->BSRR |= (GPIO_BSRR_BS_0 << (PIN_NO));
}

void pinWrite(pinNo_t pinNo, uint8_t state)
{
	if(state) {
		PIN_BASE->BSRR |= (GPIO_BSRR_BS_0 << (PIN_NO));
		return;
	}
		PIN_BASE->BSRR |= (GPIO_BSRR_BR_0 << (PIN_NO));
}

//Enable the pin port's clock
//in this family of MCU ell the cloks are on the same bus that is why we dion't need to 
//modfify the AHBENR.
//DS Page : 121 
void pinInit(pinNo_t pinNo)
{
	RCC->AHBENR |= pinPort[PIN_PORT];
}

//in this family of MCU ell the cloks are on the same bus that is why we dion't need to 
//modfify the AHBENR.
//DS Page : 121 
void pinDeInit(pinNo_t pinNo)
{
	RCC->AHBENR &=~(pinPort[PIN_PORT]);
}


void pinReset(pinNo_t pinNo)
{

}

void pinHardwareInfo(pinNo_t pinNo)
{
	//TODO : define where to print anh woh to print	
}

void pinThrowError(pinErrors_t error)
{
	while(1);
}
 	


