#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include <avr/pgmspace.h>
#include <stdlib.h>
#include <stddef.h>

#include "uart.h"
#include "fifo.h"
#include "util.h"

void init_clk(void)
{

    // ========= System Clock configuration =========
    // Set to external 16Mhz crystal, using the PLL at *2
    // set it to be a 12-16Mhz crystal with a slow start-up time.
    OSC.XOSCCTRL = OSC_FRQRANGE_2TO9_gc | OSC_XOSCSEL_XTAL_16KCLK_gc ;
    OSC.CTRL |= OSC_XOSCEN_bm ; // enable it
    while( (OSC.STATUS & OSC_XOSCRDY_bm) == 0 ){} // wait until it's stable

    // The external crystal is now running and stable.
    // (Note that it's not yet selected as the clock source)
    // Now configure the PLL to be eXternal oscillator * 2
    OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2 ;

    // now enable the PLL...
    OSC.CTRL |= OSC_PLLEN_bm ; // enable the PLL...
    while( (OSC.STATUS & OSC_PLLRDY_bm) == 0 ){} // wait until it's stable

    // And now, *finally*, we can switch from the internal 2Mhz clock to the PLL
    CCP = CCP_IOREG_gc;	// protected write follows   
    CLK.CTRL = CLK_SCLKSEL_PLL_gc;	// The System clock is now  PLL (16Mhz * 2)
    // ==============================================
}

void adc_init(void){
    ADCA.CTRLB = 0x00;
    ADCA.REFCTRL = ADC_REFSEL_INT1V_gc;
    ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;

    uint8_t CalibrationByteL;
    uint8_t CalibrationByteH;
    NVM_CMD = NVM_CMD_READ_CALIB_ROW_gc;
    CalibrationByteL = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0));
    CalibrationByteH = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1));
    NVM_CMD = NVM_CMD_NO_OPERATION_gc;
    ADCA.CALL = CalibrationByteL;
    ADCA.CALH = CalibrationByteH;

    ADCA.CTRLA = ADC_ENABLE_bm;

    ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
    ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
//
//    ADCA.CH1.CTRL = ADC_CH_INPUTMODE1_bm;
//    ADCA.CH1.MUXCTRL = ADC_CH_MUXNEG_PIN1_gc;
//
}

uint16_t adc_read(void){
    ADCA.CH0.CTRL |= ADC_CH_START_bm;
    while(!(ADCA.CH0.INTFLAGS & ADC_CH_CHIF_bm));
    ADCA.CH0.INTFLAGS = ADC_CH_CHIF_bm;
    return ADCA.CH0.RES;
}

uint16_t adc1_read(void){
    ADCA.CH1.CTRL |= ADC_CH_START_bm;
    while(!(ADCA.CH1.INTFLAGS & ADC_CH_CHIF_bm));
    ADCA.CH1.INTFLAGS = ADC_CH_CHIF_bm;
    return ADCA.CH1.RES;
}

int main (void)
{
    init_clk();
    init_uart();

    adc_init();

    sei();
    PMIC_CTRL = PMIC_LOLVLEN_bm;
    PORTA.DIR = 0;

    uint32_t val;
    //uint32_t val1;

    printf("moin!\r\n");

    while(1)
    {
        val = adc_read();
        val = val * 1000 / 4096;
        printf("val:  %03dmV\r\n", val);

        //printf("val1: %03d\r\n", val1);
        printf("\r\n");
        delay(1000);
    }
}
