#include "STM32F4xx.h"
#include "_mcpr_stm32f407.h"
#include "spi_oled.h"
#include "tools.h"
#include "timer.h"

void spiInit(void) {
	//CONFIGURE I/Os FOR SPI2
	//SCLK = PB13
	setOutputPort(B, 13, ALTERNATIVE, PUSHPULL, HIGH, LOWSTATE);
	//AFRH Register: Select AF5(SPI2_SCK, DM00037051 S. 61) on PB13 (4 Bits wide per pin, High Register contains D8..15)
	GPIOB->AFR[1] &= 0xFF0FFFFF; //clear the to D13 corresponding 4 Bit Field
	GPIOB->AFR[1] |= 0x00500000; //Write "5" (=AF5) to the D13 4 Bit Field
	
	//MOSI = PC3
	setOutputPort(C, 3, ALTERNATIVE, PUSHPULL, HIGH, LOWSTATE);
	//AFRH Register: Select AF5(SPI2_MOSI, DM00037051 S. 61) on PC3 (4 Bits wide per pin, High Register contains D8..15)
	GPIOC->AFR[0] &= 0xFFFF0FFF; //clear the to C3 corresponding 4 Bit Field
	GPIOC->AFR[0] |= 0x00005000; //Write "5" (=AF5) to the C3 4 Bit Field
	
	//CS = PE1
	setOutputPort(E, 1, OUTPUT, PUSHPULL, HIGH, HIGHSTATE);
	
	//DC = PE2
	setOutputPort(E, 2, OUTPUT, PUSHPULL, HIGH, LOWSTATE); //high->data, low->cmd
	
	//RST = PE4
	setOutputPort(E, 4, OUTPUT, PUSHPULL, HIGH, LOWSTATE); //hold in reset
	//just for security reasons...
	delayMs(1);
	
	//CONFIGURE THE SPI2 BUS
	//Enable Clock for SPI2@APB1 (Bit 14)
	RCC->APB1ENR |= 0x4000;
	//SPI2_CR1: BR, Bit 3-5: Baudrate, set to "011" (fPCLK/16 = 84(?)/16 = 5,25 Mhz)
	SPI2->CR1 &= 0xFFC7;
	SPI2->CR1 |= 0x18;
	//SPI2_CR1: CPOL, Bit 1: "1" CK to 1 when idle
	SPI2->CR1 |= 0x2;
	//SPI2_CR1: CPHA, Bit 0: "0": The first clock transition is the first data capture edge
	SPI2->CR1 &= 0xFFFE;
	//SPI2_CR1: DFF, Bit 11: "0": 8-bit data frame format is selected for transmission/reception
	SPI2->CR1 &= 0xF7FF;
	//SPI2_CR1: LSBFIRST, Bit 7: "0": MSB transmitted first
	SPI2->CR1 &= 0xFF7F;

	//SPI2_CR2: FRF, Bit 4: "1" SPI TI mode (needed????)
	SPI2->CR2 |= 0x10;
	
	//SPI2_CR1: MSTR, Bit 2: "1" Master configuration
	SPI2->CR1 |= 0x4;
	//SPI2_CR1: SPE, Bit 6: "1" Peripheral enabled
	SPI2->CR1 |= 0x40;

	//Release Display from Reset
	setOutputPortState(E, 4, HIGHSTATE);
}

void oledInit(void) {
	//oledSendCmd_enum(DisplayOn);
	//oledSendCmd_enum(EntireDisplayOnResume);
	oledSendCmd_enum(DisplayOff);
	oledSendCmd_enum(ClockDivide);
	oledSendCmd(0x80);
	oledSendCmd_enum(Multiplex);
	oledSendCmd(0x3F);
	oledSendCmd_enum(DisplayOffset);
	oledSendCmd(0x0);
	oledSendCmd_enum(StartLine0);
	oledSendCmd_enum(ChargePump);
	oledSendCmd(0x10); //externalVCC, otherwise 0x14
	oledSendCmd_enum(MemoryAddressing);
	oledSendCmd(0x0);
	oledSendCmd_enum(SegRemap127);
	oledSendCmd_enum(ComScanDec);
	oledSendCmd_enum(ComPins);
	oledSendCmd(0x12);
	oledSendCmd_enum(Contrast);
	oledSendCmd(0x9F); //externalVCC, otherwise 0xCF
	oledSendCmd_enum(PreCharge);
	oledSendCmd(0x22); //externalVCC, otherwise 0xF1
	oledSendCmd_enum(DeselectLevel);
	oledSendCmd(0x40);
	oledSendCmd_enum(EntireDisplayOnResume);
	oledSendCmd_enum(DisplayNormal);
	oledSendCmd_enum(DisplayOn);
}

void spiSend(unsigned char data) {
	//bad idea... testing:
	while ((SPI2->SR & 0x80) | !(SPI2->SR & 0x2)); //BUSY Flag set? TXE set? (TX in progress)
	//write data to DR (lowbyte cause 8 Bit format), transmission starts automatically
	SPI2->DR = data;
	//again bad idea, but lets wait till transmission is completed
	while ((SPI2->SR & 0x80) | !(SPI2->SR & 0x2)); //BUSY Flag set? TXE set? (TX in progress)
}

void oledSendData(unsigned char data) {
	//CS to low
	setOutputPortState(E, 1, LOWSTATE);
	//DC -> Data=high
	setOutputPortState(E, 2, HIGHSTATE);
	//Send data
	spiSend(data);
	//CS to high
	setOutputPortState(E, 1, HIGHSTATE);
}

void oledSendCmd(unsigned char cmd) {
	//CS to low
	setOutputPortState(E, 1, LOWSTATE);
	//DC -> Command=low
	setOutputPortState(E, 2, LOWSTATE);
	//Send data
	spiSend(cmd);
	//CS to high
	setOutputPortState(E, 1, HIGHSTATE);
}

void oledSendCmd_enum(enum Command cmd) {
	//CS to low
	setOutputPortState(E, 1, LOWSTATE);
	//DC -> Command=low
	setOutputPortState(E, 2, LOWSTATE);
	//Send data
	spiSend(cmd);
	//CS to high
	setOutputPortState(E, 1, HIGHSTATE);
}

