/*
 * main.c
 *
 *  Created on: 16 mrt. 2021
 *      Author: Maxhu
 *
 */
#include "main.h"

#define RCC 0x40021000 //RCC (Reset and Clock Control)

#define AHBENR 0x14 // AHB peripheral clock enable register
#define APB1ENR 0x1C // APB peripheral clock enable register 1
#define BDCR 0x20 // RTC domain control register

#define RCC_AHBENR (*(unsigned int *)(RCC + AHBENR))
#define RCC_APB1ENR (*(unsigned int *)(RCC + APB1ENR))
#define RCC_BDCR (*(unsigned int *)(RCC + BDCR))

#define PWR 0x40007000

#define CR 0x00 // Power control register

#define PWR_CR (*(unsigned int *)(PWR + CR))

#define RTC 0x40002800

#define WPR 0x24 // RTC write protection register
#define ISR 0x0C // RTC initialization and status register
#define TR 0x00 // RTC time register

#define RTC_WPR (*(unsigned int *)(RTC + WPR))
#define RTC_ISR (*(unsigned int *)(RTC + ISR))
#define RTC_TR (*(unsigned int *)(RTC + TR))

#define GPIOA 0x48000000
#define GPIOB 0x48000400

#define MODER 0x00 // Pin mode register
#define ODR 0x14 // Output state register
#define IDR 0x10 // Input state register
#define PUPDR 0x0C // Pull-up/pull-down register

#define GPIOA_MODER (*(unsigned int *)(GPIOA + MODER))
#define GPIOA_ODR (*(unsigned int *)(GPIOA + ODR))
#define GPIOA_PUPDR (*(unsigned int *)(GPIOA + PUPDR))
#define GPIOA_IDR (*(unsigned int *)(GPIOA + IDR))


#define GPIOB_MODER (*(unsigned int *)(GPIOB + MODER))
#define GPIOB_ODR (*(unsigned int *)(GPIOB + ODR))
#define GPIOB_PUPDR (*(unsigned int *)(GPIOB + PUPDR))
#define GPIOB_IDR (*(unsigned int *)(GPIOB + IDR))

#define TIM3 0x40000400

#define CR1 0x00 // Control register 1
#define PSC 0x28 // Prescaler
#define CNT 0x24 // Counter value
#define EGR 0x14 // Event generation register
#define CCR1 0x34 // Capture/compare register 1
#define SR 0x10 // Status register
#define CCMR1 0x18 // Capture/compare mode register 1

#define TIM3_CR1 (*(unsigned int *)(TIM3 + CR1))
#define TIM3_PSC (*(unsigned int *)(TIM3 + PSC))
#define TIM3_CNT (*(unsigned int *)(TIM3 + CNT))
#define TIM3_EGR (*(unsigned int *)(TIM3 + EGR))
#define TIM3_CCR1 (*(unsigned int *)(TIM3 + CCR1))
#define TIM3_SR (*(unsigned int *)(TIM3 + SR))
#define TIM3_CCMR1 (*(unsigned int *)(TIM3 + CCMR1))

#define HIGH 1
#define LOW 0
#define CLK 1
#define DIO 0
#define RUNNING 1
#define PAUSED 0

int numbers[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
int count = LOW;
int state = PAUSED;
int cleared = LOW;

void shieldConfig() {
	RCC_AHBENR |= (1 << 17); // Enable clock for GPIO port A
	RCC_AHBENR |= (1 << 18); // Enable clock for GPIO port B

	RCC_APB1ENR |= (1 << 1); // Enable clock for TIM3

	// Set GPIO port A pin 5 to output (CLK)
	GPIOA_MODER &= ~(1 << 11);
	GPIOA_MODER |= (1 << 10);

	// Set GPIO port A pin 6 to output (DIO)
	GPIOA_MODER &= ~(1 << 13);
	GPIOA_MODER |= (1 << 12);

	// Set GPIO port A pin 3 to output (LED0)
	GPIOA_MODER &= ~(1 << 7);
	GPIOA_MODER |= (1 << 6);

	// Set GPIO port A pin 7 to input with pull-up (BUTT)
	GPIOA_MODER &= ~(1 << 15);
	GPIOA_MODER &= ~(1 << 14);

	GPIOA_PUPDR &= ~(1 << 15);
	GPIOA_PUPDR |= (1 << 14);

	// Set TIM3 prescale, set update generation and set state to up-counter.
	TIM3_PSC = 8000 - 1;
	TIM3_EGR |= (1 << 0);
	TIM3_CR1 &= ~(1 << 4);
}

int buttonRead() {
	if ((GPIOA_IDR & (1 << 7)) == 0) {
		return HIGH;
	} else if ((GPIOA_IDR & (1 << 7)) == 1) {
		return LOW;
	}

	return 0;
}

void ledWrite(int num, int state) {
	switch (num) {
		case 0:
			if (state == HIGH) GPIOA_ODR |= (1 << 3);
			if (state == LOW) GPIOA_ODR &= ~(1 << 3);
			break;
		default:
			break;
	}
}

void timerDelay(int milliseconds) {
	TIM3_CCR1 = milliseconds;
	TIM3_CR1 |= (1 << 0);

	while ((TIM3_SR & (1 << 1)) == 0);

	TIM3_CR1 &= ~(1 << 0);
	TIM3_CNT = 0;
	TIM3_SR &= ~(1 << 1);
}

void clearTimer() {
	RCC_BDCR |= (1 << 0);
	RCC_BDCR |= (1 << 15); // Enable RTC clock

	// Disable RTC write protection
	RTC_WPR = 0xCA;
	RTC_WPR = 0x53;

	RTC_ISR |= (1 << 7); // Set RTC in initialization mode

	timerDelay(10);

	if (RTC_ISR == 0xC7) {
		RTC_TR = 0x00000000;
		timerDelay(1000);
	}

	RTC_ISR &= ~(1 << 7); // Set RTC out of initialization mode
}

int segments[4];
int minuteTensBits[3], minuteUnitsBits[4], secondUnitsBits[4], secondTensBits[3];
int minuteTens, minuteUnits, secondUnits, secondTens;

void setupRTC() {
	RCC_APB1ENR |= (1 << 28); // Enable clock for power interface
	PWR_CR |= (1 << 8); // Enable access to RTC

	// Select LSE as clock source
	RCC_BDCR &= ~(1 << 9);
	RCC_BDCR |= (1 << 8);

	RCC_BDCR |= (1 << 0);
	RCC_BDCR |= (1 << 15);

	clearTimer();
	cleared = HIGH;
	updateDisplay();

	RCC_BDCR &= ~(1 << 0);
	RCC_BDCR &= ~(1 << 15);

	while(1) {
		if (state == RUNNING) updateDisplay();

		if (buttonRead() == HIGH && cleared == HIGH) {
			timerDelay(900);

			if (buttonRead() == LOW) {
				if (state == PAUSED) {
					RCC_BDCR |= (1 << 0);
					RCC_BDCR |= (1 << 15);

					state = RUNNING;
				} else if (state == RUNNING) {
					RCC_BDCR &= ~(1 << 0);
					RCC_BDCR &= ~(1 << 15);

					state = PAUSED;
				}
			} else if (buttonRead() == HIGH) {
				cleared = LOW;
				clearTimer();

				updateDisplay();
				updateDisplay();

				RCC_BDCR &= ~(1 << 0);
				RCC_BDCR &= ~(1 << 15);

				state = PAUSED;

				timerDelay(2000);
				cleared = HIGH;
			}
		}
	}
}

void updateDisplay() {
	minuteTens = 0x0;
	minuteUnits = 0x0;
	secondUnits = 0x0;
	secondTens = 0x0;

	minuteTensBits[0] = RTC_TR & (1 << 12);
	minuteTensBits[1] = RTC_TR & (1 << 13);
	minuteTensBits[2] = RTC_TR & (1 << 14);

	minuteUnitsBits[0] = RTC_TR & (1 << 8);
	minuteUnitsBits[1] = RTC_TR & (1 << 9);
	minuteUnitsBits[2] = RTC_TR & (1 << 10);
	minuteUnitsBits[3] = RTC_TR & (1 << 11);

	secondTensBits[0] = RTC_TR & (1 << 4);
	secondTensBits[1] = RTC_TR & (1 << 5);
	secondTensBits[2] = RTC_TR & (1 << 6);

	secondUnitsBits[0] = RTC_TR & (1 << 0);
	secondUnitsBits[1] = RTC_TR & (1 << 1);
	secondUnitsBits[2] = RTC_TR & (1 << 2);
	secondUnitsBits[3] = RTC_TR & (1 << 3);

	for (int m = 0; m < 3; m++) {
		if (minuteTensBits[m]) {
			minuteTens |= 1 << m;
		}

		if (secondTensBits[m]) {
			secondTens |= 1 << m;
		}
	}

	for (int i = 0; i < 4; i++) {
		if (minuteUnitsBits[i]) {
			minuteUnits |= 1 << i;
		}

		if (secondUnitsBits[i]) {
			secondUnits |= 1 << i;
		}
	}

	segments[0] = minuteTens;
	segments[1] = minuteUnits;
	segments[2] = secondTens;
	segments[3] = secondUnits;

	setSegments(segments);
}

void clkWrite(int state) {
	if (state == HIGH) GPIOA_ODR |= (1 << 5); // Read pin 5 HIGH
	if (state == LOW) GPIOA_ODR &= ~(1 << 5); // Read pin 5 LOW
}

void dioWrite(int state) {
	if (state == HIGH) GPIOA_ODR |= (1 << 6); // Read pin 6 HIGH
	if (state == LOW) GPIOA_ODR &= ~(1 << 6); // Read pin 6 LOW
}

void start() {
	clkWrite(HIGH);
	dioWrite(HIGH);
	dioWrite(LOW);
	clkWrite(LOW);
}

void stop() {
	clkWrite(LOW);
	dioWrite(LOW);
	clkWrite(HIGH);
	dioWrite(HIGH);
}

void writeByte(int byte) {
	int count;
	int data = byte;

	for (int i = 0; i < 8; i++) {
		clkWrite(LOW);

		if (data & 0x01) {
			dioWrite(HIGH);
		} else {
			dioWrite(LOW);
		}

		clkWrite(HIGH);

		data = data >> 1;
	}

	clkWrite(LOW);
	dioWrite(HIGH);
	clkWrite(HIGH);

	// Set GPIO port A pin 6 to output (DIO)
	GPIOA_MODER &= ~(1 << 13);
	GPIOA_MODER |= (1 << 12);

	while ((GPIOA_IDR & (1 << 6)) == 1) {
		count += 1;

		if (count == 200) {
			// Set GPIO port A pin 6 to output (DIO)
			GPIOA_MODER &= ~(1 << 13);
			GPIOA_MODER |= (1 << 12);

			dioWrite(LOW);
			count = 0;
		}

		// Set GPIO port A pin 6 to output (DIO)
		GPIOA_MODER &= ~(1 << 13);
		GPIOA_MODER |= (1 << 12);
	}

	// Set GPIO port A pin 6 to output (DIO)
	GPIOA_MODER &= ~(1 << 13);
	GPIOA_MODER |= (1 << 12);
}



void setSegments(int segments[]) {
	for (int i = 0; i < 4; i++) {
		int number = numbers[segments[i]];
		int position = 0xC0 + i;

		if (i == 1) {
			number |= 0x80;
		}

		start();
		writeByte(0x40);
		stop();

		start();
		writeByte(position);
		writeByte(number);
		stop();

		start();
		writeByte(0x88); // 4-bit brightness
		stop();
	}
}

int main() {
	shieldConfig();
	setupRTC();
}



