/* Name: main.c
 * Project: StepAttenuator based on AVR USB driver by ObDev / Christian Starkjohann
 * Author: DG8SAQ
 * Creation Date: 2016-11-09
 * Tabsize: 4
 * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
 * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
 * This Revision: $Id: main.c 485 2008-02-05 15:07:28Z cs $
 */

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <avr/eeprom.h>

unsigned char EEMEM nvOSCAL;			//RC oscillator calibration value
unsigned char EEMEM nvSERIAL;			//serial number of device (default 255)
unsigned char usb_connected=0;
unsigned long int sysclock=8000000ul;	//default system clock 8MHz, usb connectivity will change this to 12,8MHz

#include "defs.h"
#include "usbdrv/usbdrv.h"
#include "LCD.h"
#include "ADC.h"
#include "relays.h"
#include "turnwheel.h"
#include "timer.h"
#include "device.h"
#include "UART.h"



/* ------------------------------------------------------------------------- */
/* ------------------------ Oscillator Calibration ------------------------- */
/* ------------------------------------------------------------------------- */

/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
 * derived from the 66 MHz peripheral clock by dividing. Our timing reference
 * is the Start Of Frame signal (a single SE0 bit) available immediately after
 * a USB RESET. We first do a binary search for the OSCCAL value and then
 * optimize this value with a neighboorhod search.
 * This algorithm may also be used to calibrate the RC oscillator directly to
 * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
 * is wide outside the spec for the OSCCAL value and the required precision for
 * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
 * experimental purposes only!
 */
static void calibrateOscillator(void)
{
uchar       step = 128;
uchar       trialValue = 0, optimumValue;
int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);

    /* do a binary search: */
    do{
        OSCCAL = trialValue + step;
        x = usbMeasureFrameLength();    /* proportional to current real frequency */
        if(x < targetValue)             /* frequency still too low */
            trialValue += step;
        step >>= 1;
    }while(step > 0);
    /* We have a precision of +/- 1 for optimum OSCCAL here */
    /* now do a neighborhood search for optimum value */
    optimumValue = trialValue;
    optimumDev = x; /* this is certainly far away from optimum */
    for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
        x = usbMeasureFrameLength() - targetValue;
        if(x < 0)
            x = -x;
        if(x < optimumDev){
            optimumDev = x;
            optimumValue = OSCCAL;
        }
    }
    OSCCAL = optimumValue;
}
/*
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
the optimum value is far below 192. It may therefore exceed the allowed clock
frequency of the CPU in low voltage designs!
You may replace this search algorithm with any other algorithm you like if
you have additional constraints such as a maximum CPU clock.
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
both regions.
*/

void    usbEventResetReady(void)
{
    calibrateOscillator();
    eeprom_update_byte(&nvOSCAL, OSCCAL);   /* store the calibrated value in EEPROM */
	usb_connected=1;
}

USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) 
{
return 0;
}

USB_PUBLIC uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;
static uchar    replyBuf[17];
unsigned char i;
    usbMsgPtr = replyBuf;
    if(rq->bRequest == 0){  /* ECHO value */
        replyBuf[0] = rq->wValue.bytes[0];
        replyBuf[1] = rq->wValue.bytes[1];
        return 2;
    }


    if(rq->bRequest == 0x1){  /* value to I2C */
		i2c_outbyte(rq->wValue.bytes[0]);
		replyBuf[0] =I2CErrors;
        return 1;
    }

    if(rq->bRequest == 0x10){  /* value to UART TX buffer */
		TXBuf[TX2]=rq->wValue.bytes[0];
		TX2++;
		if (TX2>31) TX2=31;
        return 0;
    }
    if(rq->bRequest == 0x11){  /* reset UART TX buffer */
		TX2=0;
		TX1=0;
        return 0;
    }
    if(rq->bRequest == 0x12){  /* return RXBuf */
	    for (i=0;i<17;i++)
        	replyBuf[i]=RXBuf[i];
        return 17;
    }
    if(rq->bRequest == 0x13){  /* reset UART RX buffer */
		RX1=0;
		for (i=0;i<32;i++)TXBuf[i]=0;
        return 0;
    }
    if(rq->bRequest == 0x14){  /* set RX_Idle */
        RX_Idle=rq->wValue.bytes[0]&1;
        return 0;
    }
    if(rq->bRequest == 0x20){  /* set attenuator */
        atten=rq->wValue.bytes[0];
		menu=MENU_ATT;
		select=0;
		channel='U';
        return 0;
    }
    if(rq->bRequest == 0x25){  /* switch Bluetooth on/off */
        Bluetooth(rq->wValue.bytes[0]);
		menu=MENU_BT_OFF;
		select=0;
		channel='U';
        return 0;
    }
    if(rq->bRequest == 0x26){  /* set serial number */
        eeprom_update_byte(&nvSERIAL,rq->wValue.bytes[0]);
        return 0;
    }

    if(rq->bRequest == 0x80){  /* return min Att */
        replyBuf[0]=MIN_ATTEN;
        return 1;
    }
    if(rq->bRequest == 0x81){  /* return max Att */
        replyBuf[0]=MAX_ATTEN;
        return 1;
    }
    if(rq->bRequest == 0x82){  /* return Min Step */
        replyBuf[0]=MIN_STEP;
        return 1;
    }
    if(rq->bRequest == 0x83){  /* return Att */
        replyBuf[0]=atten;
        return 1;
    }
    if(rq->bRequest == 0x84){  /* return step */
        replyBuf[0]=pgm_read_byte(&steps[step]);
        return 1;
    }    
	if(rq->bRequest == 0x85){  /* return Bluetooth on status */
        replyBuf[0]=BT_on;
        return 1;
    }
    if(rq->bRequest == 0x86){  /* read serial number */
        replyBuf[0]=eeprom_read_byte(&nvSERIAL);
        return 1;
    }
    return 0;
}


/*------------------------------------------------------------------------------*/
/*------------------------       Main       ------------------------------------*/
/*------------------------------------------------------------------------------*/


int main(void)
{
#ifndef NO_I2C_PORTEXPANDER
	i2c_outbyte(0);
#endif
	wdt_disable();
/////////////////////////////////////////////////////////////////////////////////////
//	Power up sequence
	BT_Init();
	InitPower();	
/////////////////////////////////////////////////////////////////////////////////////
//  usbConnect sequence start
    int i;
    usbDeviceDisconnect();
    for(i=0;i<60;i++){  		// 900 ms disconnect for letting switching regulator power up before connecting to USB, otherwise interferences
        _delay_ms(15);
    	}
    usbDeviceConnect();
//  usbConnect sequence done
/////////////////////////////////////////////////////////////////////////////////////
//	check if USB is connected	
	wdt_enable(WDTO_1S);		//set Watchdog Timer
    usbInit();
	sei();
	for (i=0;i<500;i++){
		_delay_us(500);
		wdt_reset();
        usbPoll();
		}
	if (usb_connected==1) sysclock=12800000ul; else sysclock=8000000ul;	  //set sysclock variable depending on connect state	
	if (usb_connected==0) USB_INTR_ENABLE &= ~(1 << USB_INTR_ENABLE_BIT); //disable usb interrupt
/////////////////////////////////////////////////////////////////////////////////////
//	init sequence
	INIT_POWER_PIN;
	wdt_reset();
	uart_init();
	ADC_Init();
	TurnWheelInit();
	InitDev();
	InitAtten();
/////////////////////////////////////////////////////////////////////////////////////
//	main loop
    for(;;){    				// main event loop 
	    wdt_reset();			//restart watchdog timer
		ADC_Poll();
		uart_send_poll();
        usbPoll();
    }
}

