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

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

#define TEPLOTA_VRCHOLU       216
#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 signed int8  hod;
   volatile signed int8  min;
   volatile signed 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;
float temp_slope=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_64);  //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_TIMER0);
   
   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
{
  top_heat_power=0;
  bottom_heat_power=0;

  lcd_gotoxy(1,2);
  printf(lcd_putc,"HEATING FAILURE!");
  
  while(true);

}

unsigned int16 adc(void) // adc read and filtering
{
   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)           // temperature measuring
{
   return (0.674201*adc() - 294.35);  // temperature calculaton (linear aproximation)
}

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_TIMER0
void heating_control()        //rizeni topnych teles pri preteceni casovace
{
float temp;

  top_heating();
  bottom_heating();
   
  
  if ((period == 100) || (period == 0))
  {
    temp=teplota();
    temp_slope=(temp - temp_last) /(100.0*256.0/62500.0); // vypocet strmosti narustu teploty ve stupnich/s
    temp_last = temp;
    printf("%02u %02u %3.3f \r\n",cas.min,cas.sec,temp); //vypis pro zaznam profilu
  }
  
  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 = temp_slope - ramp;          // vypocet strmosti a odchylky od pozadovane strmosti

  if(slope_deviation < 0)
  {
    top_heat_power= 60 + balance;
    bottom_heat_power= 100;
  }
  else{
    top_heat_power=0;
    bottom_heat_power=0;
  }
}

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");
  printf("#PREHEAT\r\n");

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

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

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

    lcd_gotoxy(10,2);
    printf(lcd_putc,"%1.1f\21C/s  ",temp_slope);

    delay_ms(200);
    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_clr();
  lcd_gotoxy(1,2);
  printf(lcd_putc,"SOAK    ");
  printf("#SOAK\r\n");

  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,"%02u:%02u", process_time.min, process_time.sec);
    delay_ms(200);
  }
  
//----------------- solder ----------------------------

  nullcas(&cas);
  lcd_clr();  
  lcd_gotoxy(1,2);
  printf(lcd_putc,"SOLDER");
  printf("#SOLDER\r\n");

  do {
    slope_control(SOLDER_SLOPE, 10); // hlida strmost predehrevu 

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

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

    lcd_gotoxy(10,2);
    printf(lcd_putc,"%1.1f\21C/s  ",temp_slope);

    delay_ms(200);
    if (cas.min>2) heat_failure();
  }
  while (teplota() < SOLDER_TEMP);

// ---------------- TAO ------------------------


  while (process_time.sec!=0 || process_time.min!=0)
  {
    level_control(SOLDER_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 =  (SOLDER_TIME - cas.min*60 - cas.sec)/60;
    process_time.sec = (SOLDER_TIME - cas.min*60 - cas.sec) - process_time.min*60;

    lcd_gotoxy(9,1);
    printf(lcd_putc,"%02u:%02u", process_time.min, process_time.sec);
    
    delay_ms(200);
    
  }

// ---------------- COOLING ------------------------

  top_heat_power=0;
  bottom_heat_power=0;

  lcd_clr();

  lcd_gotoxy(1,2);
  printf(lcd_putc,"REFLOW COMPLETE");
  printf("COOLING \r\n");


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

    lcd_gotoxy(10,1);
    printf(lcd_putc,"%1.1f\21C/s  ",temp_slope);
  }
}

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