#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_usart.h"
#include "misc.h"
#include 
#include 

// easy to remember names for Pins 12, 13, 14
#define RedLED 0x1000
#define GreenLED 0x2000
#define BlueLED 0x4000

// create initialization structures
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

// define RGB color type
typedef struct {
	uint8_t R, G, B;
} ColorTypeDef;

ColorTypeDef ColorStructure; // create color type instance

uint32_t ISRCount = 0; // counts interrupt service routines
uint32_t total = 0; // total duration of one PWM cycle
uint32_t color = 0x000000; // storage for color value

// function prototypes
void AssignColor(ColorTypeDef * ColorStructure, uint32_t TargetColor);
void ColorPWM(ColorTypeDef * ColorStructure);
void InitializePeriph();
uint8_t ascii_to_hex(uint8_t ascii);

int main(void) {
	InitializePeriph();
	while(!USART_GetFlagStatus(USART3, USART_FLAG_TXE)){} // wait for empty transmit buffer
	printf("RGB LED Control with HTML Color Codes!");
	while(!USART_GetFlagStatus(USART3, USART_FLAG_TXE)){} // wait for empty transmit buffer

	while (1) {ColorPWM(&ColorStructure);} // constantly repeats PWM cycle
}

/* InitializePeriph() prepares all of the STM32's peripherals for this project
 * Required peripherals are GPIO and USART
 * NVIC assigns an interrupt service routine to an incoming USART message
 */
void InitializePeriph(){
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

	// Set GPIO pins Alt. Function to USART1
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);

	// PC10: USART TX, PC11: USART RX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 + GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);

	// PD12, PD13, PD14: Outputs for R, G, B
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 + GPIO_Pin_13 + GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);

	// Initialize USART
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_Init(USART3, &USART_InitStructure);
    USART_Cmd(USART3,ENABLE);

    // Assign NVIC to USART3
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
}

/* Incoming UART messages will trigger an interrupt. The ISR keeps track of
* the incoming color code and updates the color structure as it receives
* characters.
*/
void USART3_IRQHandler(void)
{
	uint8_t hex = 0;
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET){
        hex = ascii_to_hex((char)USART_ReceiveData(USART3));
    }

    if (ISRCount % 6 == 0) color = 0;

    color +=  hex << 4*(5 - (ISRCount % 6));
    ISRCount++;
    AssignColor(&ColorStructure, color);
}

/* ascii_tohex() takes an ascii character and translates it
 * to the corresponding hexadecimal value
 */
uint8_t ascii_to_hex(uint8_t ascii){
	uint8_t hex = 0;
	switch (tolower(ascii)){
		case 'f': hex = 0xf; break;
		case 'e': hex = 0xe; break;
		case 'd': hex = 0xd; break;
		case 'c': hex = 0xc; break;
		case 'b': hex = 0xb; break;
		case 'a': hex = 0xa; break;
		case '9': hex = 0x9; break;
		case '8': hex = 0x8; break;
		case '7': hex = 0x7; break;
		case '6': hex = 0x6; break;
		case '5': hex = 0x5; break;
		case '4': hex = 0x4; break;
		case '3': hex = 0x3; break;
		case '2': hex = 0x2; break;
		case '1': hex = 0x1; break;
		case '0': hex = 0x0; break;
		default: hex = 0x0; break;
		    }
	return hex;
}

/* AssignColor() takes a html color code and parses it into
 * Red, Green and Blue values
 */
void AssignColor(ColorTypeDef * ColorStructure, uint32_t TargetColor){

	ColorStructure->R = (TargetColor&0x00FF0000)/0x10000;
	ColorStructure->G = (TargetColor&0x0000FF00)/ 0x100;
	ColorStructure->B = (TargetColor&0x000000FF)/0x1;
}

/* ColorPWM applies the RGB values to PWM pulse widths and apply
 * the appropriate signals to the GPIO
 */
void ColorPWM(ColorTypeDef * ColorStructure){

	uint16_t rLimit = (ColorStructure->R + ColorStructure->G + ColorStructure->B);
	uint16_t gLimit = (ColorStructure->G + ColorStructure->B);
	uint8_t bLimit = (ColorStructure->B);

	uint8_t r, g, b;

	for (uint16_t scan = 0; scan <= 0x0300; scan++){
		if (scan < rLimit && scan >= gLimit && scan >= bLimit){
			g = 0;
			b = 0;
			r = 1;
		}
		else if (scan < gLimit && scan >= bLimit){
			b = 0;
			r = 0;
			g = 1;
		}
		else if (scan < bLimit){
			r = 0;
			g = 0;
			b = 1;
		}
		else if (rLimit == 0){
			r = 0;
			g = 0;
			b = 0;
		}
		else {
			r = 0;
			g = 0;
			b = 0;
		}

		GPIO_Write(GPIOD, (RedLED*r) | (GreenLED*g) | (BlueLED*b));
	}
}