#include "reflow.h"
#include "process.h"
#include <math.h>

// nastaveni teplot a casu
#define TEPLOTA_PREDEHREVU    120
#define DOBA_PREDEHREVU       60

#define TEPLOTA_VRCHOLU       210
#define DOBA_VRCHOLU          5

// CPU IO rozhrani
#define LCD_RS          PIN_C1      // rizeni registru LCD displeje
#define LCD_E           PIN_C2      // enable LCD displeje
#define LCD_DATA_LSB    PIN_D0      // data LCD
#include "lcd.c"

#define TL1             PIN_B3      // tlacitko S1
#define TL2             PIN_B2      // tlacitko S2
#define TL3             PIN_B1      // tlacitko S3
#define TL4             PIN_B0      // tlacitko S4

#define POWER_T3        PIN_C4      // ovladani optotriaku T3
#define POWER_T4        PIN_C5      // ovladani optotriaku T4
#define POWER_T5        PIN_C6      // ovladani optotriaku T5

#define ADC_PIN         PIN_A0   //info, nelze menit - pin pouzit jako input analog
#define ADC_PIN_NC      PIN_A1   //info, nelze menit - pin pouzit jako input analog
#define REF_PIN         PIN_A3   //info, nelze menit - pin pouzit jako input reference 2.5V

// interni
#define PowerOn()    output_low(POWER_T4);output_low(POWER_T5)
#define PowerOff()   output_high(POWER_T4);output_high(POWER_T5)

// globalni promenne
struct time
{
   volatile unsigned int8  hod;
   volatile unsigned int8  min;
   volatile unsigned int8  sec;
}cas;

unsigned int top_heat_power=0;  // range 0-200% nad 100% je ale teleso jiz pretizene
unsigned int bottom_heat_power=0; // contains heating power range 0-100%
unsigned int period;

float temp_last=0;

void GeneralCpuInit() // inicializace
{
   output_high(POWER_T4);
   output_high(POWER_T5);
   port_b_pullups(true);
    
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);  //nepouzit
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);               // rizeni
   setup_timer_2(T2_DIV_BY_16,249,10);       //rtc 40ms
   
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER2);
   enable_interrupts(INT_TIMER1);
   
   setup_adc_ports(AN0_AN1_VSS_VREF);        //A0 vstup cidla, A1 nepozit, A3 - ref. 2.5V
   setup_adc(ADC_CLOCK_DIV_8);                
   SET_ADC_CHANNEL(0);                       //AN0,  PIN_A0  
}

void heat_failure()  // exception in case of heating fail
{
  lcd_gotoxy(1,2);
  printf(lcd_putc,"HEATING FAILURE!");
  
  while(true);

}

unsigned int16 adc(void)
{
   unsigned int16 analog;
   unsigned int8  a;

   analog = 0;
   for (a=0;a<32;a++)
   {
      analog += read_adc();
      delay_us(50);
   }
   return (analog >> 5  );         // prumer = analog/32
}

float teplota(void)
{
   return (0.674201*adc() - 294.35);
}

void top_heating()
{
  if (period < top_heat_power){
    output_low(POWER_T4);
    output_low(POWER_T5);
  }
  else{
    output_high(POWER_T4);
    output_high(POWER_T5);
  }
}

void bottom_heating()
{

  if (period < 2*bottom_heat_power){
    output_low(POWER_T3);
  }
  else{
    output_high(POWER_T3);
  }

}

#int_TIMER1
void heating_control()        //rizeni topnych teles pri preteceni casovace
{
  top_heating();
  bottom_heating();

  if (period <= 200) period++;
  else period=0;
}

#int_TIMER2
void Rtc(void)         //40ms
{
   static unsigned int8 ms40=0;
   struct time* time;
   
   time=&cas;
   if ( ++ms40 < 25) return;
   
   ms40=0;                    
    if (++(time->sec) >= 60)
    {
       time->sec=0;            //1min
        if (++(time->min) >= 60) 
      {
         time->min = 0;                    //1hod
        (time->hod)++;
      }
    }
}

void slope_control(float ramp, unsigned int balance) // P proporcionalni rizeni narustu teploty predpoklada periodicke volani 1x/s
{
float slope_deviation;

  slope_deviation = (teplota() - temp_last) - ramp;          // vypocet strmosti a odchylky od pozadovane strmosti

  if(slope_deviation < 0)
  {
    top_heat_power= 80 + balance;
    bottom_heat_power= 90;
  }
  else{
    top_heat_power=0;
    bottom_heat_power=0;
  }
  temp_last = teplota();
}

void level_control(float level) // P proporcionalni rizeni teploty
{
  if (teplota() > level)
  {
    top_heat_power=0;
    bottom_heat_power=0;
  }
  else
  {
    top_heat_power=70;
    bottom_heat_power=80;
  }
}


void nullcas(struct time* time)
{
   disable_interrupts(INT_TIMER2);
   
   time->sec=0;
   time->hod=0;
   time->min=0;
   
   enable_interrupts(INT_TIMER2);
}

void reflow_solder()
{

struct time process_time;

// preheat
  nullcas(&cas);
  lcd_gotoxy(1,2);
  printf(lcd_putc,"PREHEAT");

  do {
    slope_control(PREHEAT_SLOPE, 0); // hlida strmost predehrevu 

    lcd_gotoxy(1,1);
    printf(lcd_putc,"%3.1f\21C  ",teplota());

    lcd_gotoxy(9,1);
    printf(lcd_putc,"%2u:%02u:%02u",cas.hod,cas.min,cas.sec);

    delay_ms(1000);
    if (cas.min>3) heat_failure();
  }
  while (teplota() < SOAK_TEMP);

// soak
  nullcas(&cas);
  process_time.min = SOAK_TIME/60;
  process_time.sec = SOAK_TIME - process_time.min*60;
    
  lcd_gotoxy(1,2);
  printf(lcd_putc,"SOAK    ");

  while (process_time.sec!=0 || process_time.min!=0)
  {
    level_control(SOAK_TEMP);

    lcd_gotoxy(1,1);
    printf(lcd_putc,"%3.1f\21C  ",teplota());

    if ((process_time.sec = process_time.sec - cas.sec)<0) process_time.sec=59;

    process_time.min =  (SOAK_TIME - cas.min*60 - cas.sec)/60;
    process_time.sec = (SOAK_TIME - cas.min*60 - cas.sec) - process_time.min*60;

    lcd_gotoxy(9,1);
    printf(lcd_putc,"%2u:%02u:%02u",cas.hod, process_time.min, process_time.sec);
    delay_ms(1000);
  }
  
// solder
  
}


void main() // main loop 
{
   GeneralCpuInit();
   PowerOff();
   
   lcd_init();
   lcd_define_char(1,LCD_CHAR_STUPEN);
      
   nullcas(&cas);
   
   while(true)
   {
      delay_ms(300);            
      reflow_solder();
      
   }
}