/**
 ******************************************************************************
 * @file           : main.c
 * @author         : Auto-generated by STM32CubeIDE
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under BSD 3-Clause license,
 * the "License"; You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                        opensource.org/licenses/BSD-3-Clause
 *
 ******************************************************************************
 */

#include "stm32l073xx.h"

#if !defined(__SOFT_FP__) && defined(__ARM_FP)
  #warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif

void nastavUart(void)
{
	// zapne hodinovy signal pro port A
	RCC->IOPENR |= RCC_IOPENR_IOPAEN;

	// vynuluje nastaveni rezimu PA2 (vstupni rezim)
	GPIOA->MODER &= ~GPIO_MODER_MODE2;

	// nastavi  PA2 do alternativniho rezimu
	GPIOA->MODER |= GPIO_MODER_MODE2_1;

	// nastavi pinu PA2 alternativni rezim 4 - UART
	GPIOA->AFR[0] |= (4U << GPIO_AFRL_AFSEL2_Pos);

	// zapne hodinovy signal pro USART2
	RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

	// 2,097 MHz / 218 = 9619 Baud
	USART2->BRR = 218U;

	// povoli USART a odesilani dat
	USART2->CR1 |= (USART_CR1_UE | USART_CR1_TE);
}

void nastavAdc(void)
{
	// vydeli HSI 4x -> 4 MHz
	RCC->CR |= RCC_CR_HSIDIVEN;

	// spusti interni vysokorychlostni oscilator
	RCC->CR |= RCC_CR_HSION;

	// cekej, dokud HSI neni pripraveny
	while (!(RCC->CR & RCC_CR_HSIRDY));

	// zapne hodinovy signal pro port A
	RCC->IOPENR |= RCC_IOPENR_IOPAEN;

	// zapne hodinovy signal pro ADC1
	RCC->APB2ENR |= RCC_APB2ENR_ADCEN;

	// spusti autokalibraci (zaroven aktivuje ADC regulator)
	ADC1->CR |= ADC_CR_ADCAL;

	// cekej do konce autokalibrace
	while((ADC1->CR & ADC_CR_ADCAL) == ADC_CR_ADCAL);

	// nastavi prerusovane mereni kanalu s rozlisenim 8 bitu
	ADC1->CFGR1 |= (ADC_CFGR1_DISCEN | ADC_CFGR1_RES_1);

	// zaradi kanaly 0 do sekvence mereni
	ADC1->CHSELR |= ADC_CHSELR_CHSEL0;

	// povoli preruseni pri dokonceni jednotliveho mereni
	ADC1->IER |= ADC_IER_EOCIE;

	// povoli preruseni linky ARM jadra pro ADC1
	NVIC->ISER[0] |= (1 << ADC1_COMP_IRQn);

	// zapne prevodnik
	ADC1->CR |= ADC_CR_ADEN;
}

void nastavCasovac()
{
	// zapne hodinovy signal pro TIM6
	RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;

	// nastavi delicku pro TIM2 (2097 KHz / 2097 = 1 KHz)
	TIM6->PSC = 2096U;

	// nastavi autoreload registr pro TIM2 (1 KHz / 5 = 200 Hz)
	TIM6->ARR = 4U;

	// povoli preruseni pri preteceni (update)
	TIM6->DIER |= TIM_DIER_UIE;

	// povoli preruseni linky ARM jadra pro TIM2
	NVIC->ISER[0] |= (1U << TIM6_DAC_IRQn);
}

void spustCasovac()
{
	TIM6->CR1 |= TIM_CR1_CEN;
}

void TIM6_DAC_IRQHandler()
{
	// spusti prevod AD prevodniku
	ADC1->CR |= ADC_CR_ADSTART;

	// vynuluje priznak preruseni periferie
	TIM6->SR &= ~(TIM_SR_UIF);

	// vynuluje priznak preruseni linky
	NVIC->ICPR[0] |= (1U << TIM6_DAC_IRQn);
}

void ADC_COMP_IRQHandler()
{
	// vynuluje priznak preruseni periferie
	ADC1->ISR |= ADC_ISR_EOC;

	// vynuluje priznak preruseni linky
	NVIC->ICPR[0] |= (1U << ADC1_COMP_IRQn);

	// vypne navrat do spiciho rezimu po opusteni ISR
	SCB->SCR &= ~(1 << SCB_SCR_SLEEPONEXIT_Pos);
}

int main(void)
{
	nastavUart();

	nastavAdc();

	nastavCasovac();

	spustCasovac();

	while(1)
	{
		// zapne navrat do spiciho rezimu po opusteni ISR
		SCB->SCR |= (1 << SCB_SCR_SLEEPONEXIT_Pos);

		// zapne spici mod probuzeny prerusenim
		asm("wfi");

		uint8_t dataPoZpracovani = (ADC1->DR) / 2;

		USART2->TDR = dataPoZpracovani;
	}
}
