/*************************************

  * @file        main.c

  * @author  Vicente Jimenez

  * @version V1.0

  * @date        29/2/2012

  * @brief   Main program body

  **************************************

  *

  * Programa de control del acuario para la placa STM8L Discovery

    */

 

#define VERSION "VER 1" // 29/2/2012

#define SPLASH // Indica si se debe dar mensaje de arranque

#define PUMP_OFF_TIME 15 // Minutos que debe estar la bomba apagada despues de dar de comer

/* Includes ------------------------------*/

#include "stm8l15x.h"

#include "stm8l15x_rtc.h"

#include "stm8l15x_flash.h"

#include "stm8l_discovery_lcd.h"

#include "discover_board.h"

#include "discover_functions.h"

/* Definiciones de los botones + , -  y OK */

#define BPLUS_PORT GPIOA

#define BPLUS_PIN GPIO_Pin_2

#define BMINUS_PORT GPIOA

#define BMINUS_PIN GPIO_Pin_3

#define BOK_PORT GPIOC

#define BOK_PIN GPIO_Pin_1

/* Definiciones de las salidas de control */

#define PUMP_PORT        GPIOC

#define PUMP_PIN         GPIO_Pin_7

#define LIGHT_PORT   GPIOE

#define LIGHT_PIN        GPIO_Pin_7

#define FEED_F_PORT  GPIOC              

#define FEED_F_PIN   GPIO_Pin_0

#define FEED_R_PORT  GPIOC

#define FEED_R_PIN   GPIO_Pin_4

/* Definiciones de encendido y apagado de elementos */

#define PUMP_ON;   {BAR1_ON; PUMP_PORT->ODR|=PUMP_PIN;}           /* Nivel alto Bomba encendida */

#define PUMP_OFF;  {BAR1_OFF; PUMP_PORT->ODR&=~PUMP_PIN;}          /* Nivel bajo Bomba apagada */

#define LIGHT_ON;  {BAR0_ON; LIGHT_PORT->ODR|=LIGHT_PIN;}         /* Nivel alto Luz encendida */

#define LIGHT_OFF; {BAR0_OFF; LIGHT_PORT->ODR&=~LIGHT_PIN;}        /* Nivel bajo Luz apagada */

#define FEED_F;        {FEED_R_PORT->ODR&=~FEED_R_PIN; FEED_F_PORT->ODR|=FEED_F_PIN;}  /* Feed Motor Forward */

#define FEED_R;        {FEED_F_PORT->ODR&=~FEED_F_PIN; FEED_R_PORT->ODR|=FEED_R_PIN;}  /* Feed Motor Reverse */

#define FEED_OFF;  {FEED_F_PORT->ODR&=~FEED_F_PIN; FEED_R_PORT->ODR&=~FEED_R_PIN;}  /* Feed Motor Off */

#define ASCII_NUM_0 '0'

/* Posicion de la memoria flash de datos */

#define EEPROM_START 0x001000

#define EEPROM_L_ON  0x001001  /* Informacion de Hora/Min de Light ON  (2 bytes) */

#define EEPROM_L_OFF 0x001003  /* Informacion de Hora/Min de Light OFF (2 bytes) */

#define EEPROM_FEED  0x001005  /* Informacion de Hora/Min Feed (2 bytes) */

#define EEPROM_NFEED 0x001007  /* Numero de ciclos de alimentacion (1 byte) */

/* Codigo magico */

#define EEPROM_MAGIC 0xAB

/* Machine status used by main for active function set by user button in interrupt handler */

uint8_t state_machine;

/* LCD bar graph: used for display active function */

extern uint8_t t_bar[2];

/* Definiciones de Horas */

RTC_TimeTypeDef   RTC_TimeStr;   /* Variable para fijar la hora actual */

RTC_TimeTypeDef   RTC_Light_On;  /* Encendido de la luz */

RTC_TimeTypeDef   RTC_Light_Off; /* Apagado de la luz */

RTC_TimeTypeDef   RTC_Feed;          /* Hora de dar comida */

/* Cadena para poner la hora */

uint8_t LCDStringTime[6]="012345";

/* Flag que indica que se ha dado la comida hoy */

uint8_t feedFlag;

#define MODO_AUTOMATICO 0

#define MODO_MANUAL         1

/* Flag que indica en modo actual */

uint8_t modo;

/* Variable con el numero de ciclos para alimentar */

uint8_t feedCycles;

void Debounce(GPIO_TypeDef* GPIOx,uint8_t   GPIO_Pin)

/* Espera a que se suelte el boton y se añaden 10ms adicionales */

{

    /* Esperamos a que se suelte */

    while (!((GPIOx->IDR & GPIO_Pin)));

           

    /* Esperamos 10ms mas */

  delay_ms(10);    

}

/* Rutina para mostrar horas y minutos */

void ShowHHMM(RTC_TimeTypeDef *TimeStr)

 {

 /* Rellenamos la cadena con datos de la hora actual */

 LCDStringTime[0] = (uint8_t)(((uint8_t)(TimeStr->RTC_Hours & 0xF0) >> 4) + ASCII_NUM_0);

 LCDStringTime[1] = (uint8_t)(((uint8_t)(TimeStr->RTC_Hours & 0x0F)) + ASCII_NUM_0);

 LCDStringTime[2] = (uint8_t)(((uint8_t)(TimeStr->RTC_Minutes & 0xF0) >> 4) + ASCII_NUM_0);

 LCDStringTime[3] = (uint8_t)(((uint8_t)(TimeStr->RTC_Minutes & 0x0F)) + (uint8_t)ASCII_NUM_0);

 

 /* Escribimos en el LCD */

 

 LCD_GLASS_Clear();  /* Borramos la pantalla */

 

 LCD_GLASS_WriteChar(LCDStringTime,POINT_OFF,COLUMN_OFF,1);

 LCD_GLASS_WriteChar(LCDStringTime+1,POINT_OFF,COLUMN_ON,2);

 

 LCD_GLASS_WriteChar(LCDStringTime+2,POINT_OFF,COLUMN_OFF,3);

 LCD_GLASS_WriteChar(LCDStringTime+3,POINT_OFF,COLUMN_ON,4);

 }

 

#define HORA 1

#define MINUTO 2

/* Rutina que fija uno de los 4 digitos de HHMM

Argumentos:

          TimeStr: Puntero a una estructura de tiempo en formato BCD

                    h_m: Indica si son horas o minutos, puede valer HORA o MINUTO

                    digito: Digito a modificar 0 o 1 (0 es mayor peso)

                   

Retorna: 0 si se modifica correctamente el digito

             1 si se da timeout

*/

uint8_t SetDigit(RTC_TimeTypeDef *TimeStr,uint8_t h_m,uint8_t digito)

 {

     uint8_t i;                  /* Bucle */

     uint8_t fdev;            /* Debounce needed */

     uint8_t nibble;          /* Valor del digito */

     uint8_t pos;             /* Posicion del digito en pantalla */

     uint8_t nibble_max;   /* Maximo valor para un digito */

     uint8_t *byte_pos; /* Posicion del nibble */

   

   

     fdev=1; /* Empezamos con debounce */

   

     /* Determinamos la posicion del digito en pantalla */

     if (h_m==HORA)

               {

                             byte_pos=&(TimeStr->RTC_Hours);

                     if (digito==0) /* Primer digito de Hora */

                                           {

                                             pos=1;

                                             nibble_max=2;

                                             }

                 else           /* Segundo digito de Hora */

                 {

                                             pos=2;

                                             if ((TimeStr->RTC_Hours)<0x20)

                                                                            nibble_max=9;

                                                                            else

                                                                            nibble_max=3;

                                             /* Verificamos que la hora no sea ilegal */

                                                  if ((TimeStr->RTC_Hours)>0x23) (TimeStr->RTC_Hours)=0x23;

                                           }

                           }

                            else              

                             {

                             byte_pos=&(TimeStr->RTC_Minutes);    

                             if (digito==0) /* Primer digito de Minuto */

                 {

                                             pos=3;

                                             nibble_max=5;

                                           }

                 else           /* Segundo digito de Minuto */

                 {

                                             pos=4;

                                             nibble_max=9;

                                           }

                                   

             

                           }

           

     for(i=0;i<100;i++) /* Bucle para el timeout */

      {

            /* Parpadeo del digito que cambiamos */    

            if ((i%2)==0)    

                            ShowHHMM(TimeStr);

                            else

                            LCD_GLASS_WriteChar(" ",POINT_OFF,COLUMN_OFF,pos);    

                           

            /* Debounce if needed */

        if (fdev)

                    {

                    Debounce(BOK_PORT,BOK_PIN);

                    Debounce(BPLUS_PORT,BPLUS_PIN);

                    Debounce(BMINUS_PORT,BMINUS_PIN);

              fdev=0;

              }

   

            /* Leemos el nibble */

            if (digito)

                            nibble=((*byte_pos)&0x0F);   /* Low Nibble */

                            else

            nibble=(((*byte_pos)&0xF0)>>4);   /* High Nibble */                            

           

            /* Boton + pulsado */

            if  (!(BPLUS_PORT->IDR & BPLUS_PIN))

                                            {

                                            fdev=1; i=0;  /* Debounce y Reset de timeout */

                                            if (nibble<nibble_max) nibble++;

                                            }

   

        /* Boton - pulsado */    

            if  (!(BMINUS_PORT->IDR & BMINUS_PIN)) /* Minus */

                                            {

                                            fdev=1; i=0;  /* Debounce y Reset de timeout */

                                            if (nibble>0) nibble--;

                                      }

        /* Boton OK pulsado */

            if  (!(BOK_PORT->IDR & BOK_PIN)) return 0; /* Retorno correcto */

        /* Escribimos el nibble modificado si toca */

            if (digito)

                    (*byte_pos)=((*byte_pos)&0xF0)+nibble; /* Low Nibble */

                            else

            (*byte_pos)=((*byte_pos)&0x0F)+(nibble<<4); /* High Nibble */

                           

            delay_ms(10);            

      }

   

 return 1; /* Retorno con Timeout */            

 }

/* Rutina para elegir una hora y minutos

   

     Parametros:

             TimeStr: Estructura de la hora a modificar

                   

     Retorna: Nada

*/

void Fijar_Hora(RTC_TimeTypeDef *TimeStr)

 {

     if (SetDigit(TimeStr,HORA,0)) /* Primer digito de Hora */

              {

                            /* Timeout */

            /* Verificamos que la hora no sea ilegal */

                      if ((TimeStr->RTC_Hours)>0x23) (TimeStr->RTC_Hours)=23;                            

                            return;

                      }

                            else

                            if (SetDigit(TimeStr,HORA,1)) /* Segundo digito de Hora */

                                     return; /* Timeout */

                                             else

                                             if (SetDigit(TimeStr,MINUTO,0)) /*Primer digito de Minuto */

                                                              return; /* Timeout */

                                                                    else

                                                                    if (SetDigit(TimeStr,MINUTO,1))  /* Segundo digito de Minuto */

                                                                                    return;

                                                                                   

 /* Si todo ha ido bien, borramos los segundos */

 (TimeStr->RTC_Seconds)=0x00;

 }

 

/*

 Rutina que cambia el numero de ciclos del alimentador

*/

void Fijar_Ciclos_Alimentador()

{

    uint8_t i;                  /* Bucle */

    uint8_t car[2]="F";

    uint8_t fdev;

   

    fdev=1; /* Debounce needed */

   

    LCD_GLASS_Clear();  /* Borramos la pantalla */

   

    for(i=0;i<100;i++) /* Bucle para el timeout */

      {

                   

                    /* Debounce if needed */

          if (fdev)

                      {

                            Debounce(BOK_PORT,BOK_PIN);

                      Debounce(BPLUS_PORT,BPLUS_PIN);

                            Debounce(BMINUS_PORT,BMINUS_PIN);

                    fdev=0;

                    }

                   

                    /* Mostramos el valor actual */

                    car[0]='0'+feedCycles;

                    LCD_GLASS_WriteChar(car,POINT_OFF,COLUMN_OFF,1);

                   

                    /* Boton + pulsado */

              if  (!(BPLUS_PORT->IDR & BPLUS_PIN))

                                {

                                if (feedCycles<10) feedCycles++;

                                                    fdev=1;

                                              }

                                                   

                    /* Boton - pulsado */    

              if  (!(BMINUS_PORT->IDR & BMINUS_PIN)) /* Minus */    

                                {

                                            if (feedCycles>0) feedCycles--;

                                                    fdev=1;

                                              }

                                                   

                    /* Boton OK pulsado */

              if  (!(BOK_PORT->IDR & BOK_PIN)) return; /* Retorno correcto */    

                   

                    delay_ms(10);

            }

           

    return; /* Timeout */

}

/* Rutina de visualización de la hora */

void Time_Show()

 {

 /* Espera a la sincronizacion del calendario */

 while (RTC_WaitForSynchro() != SUCCESS);

 /* Lee la hora */

 RTC_GetTime(RTC_Format_BCD, &RTC_TimeStr);

 /* Rellenamos la cadena con datos de la hora actual */

 

 LCDStringTime[0] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Hours & 0xF0) >> 4) + ASCII_NUM_0);

 LCDStringTime[1] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Hours & 0x0F)) + ASCII_NUM_0);

 LCDStringTime[2] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Minutes & 0xF0) >> 4) + ASCII_NUM_0);

 LCDStringTime[3] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Minutes & 0x0F)) + (uint8_t)ASCII_NUM_0);

 LCDStringTime[4] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Seconds & 0xF0) >> 4) + ASCII_NUM_0);

 LCDStringTime[5] = (uint8_t)(((uint8_t)(RTC_TimeStr.RTC_Seconds & 0x0F)) + ASCII_NUM_0);

 

 

 /* Escribimos en el LCD */

 LCD_GLASS_WriteChar(LCDStringTime,POINT_OFF,COLUMN_OFF,1);

 LCD_GLASS_WriteChar(LCDStringTime+1,POINT_OFF,COLUMN_ON,2);

 

 LCD_GLASS_WriteChar(LCDStringTime+2,POINT_OFF,COLUMN_OFF,3);

 LCD_GLASS_WriteChar(LCDStringTime+3,POINT_OFF,COLUMN_ON,4);

 

 LCD_GLASS_WriteChar(LCDStringTime+4,POINT_OFF,COLUMN_OFF,5);

 LCD_GLASS_WriteChar(LCDStringTime+5,POINT_OFF,COLUMN_OFF,6);

 }

/*

 Rutina que escribe en la EEPROM la informacion de horarios

 

 EEPROM_START  

 EEPROM_L_ON        Informacion de Hora/Min de Light ON  (2 bytes)

 EEPROM_L_OFF   Informacion de Hora/Min de Light OFF (2 bytes)

 EEPROM_FEED        Informacion de Hora/Min Feed (2 bytes)

 */

void Escribir_EEPROM()

 {

     /*Define FLASH programming time*/

   FLASH_SetProgrammingTime(FLASH_ProgramTime_Standard);

     /* Desbloqueamos la memoria de Datios */

     FLASH_Unlock(FLASH_MemType_Data);

   

     /* Escribimos el codigo magico */

     FLASH_ProgramByte(EEPROM_START,EEPROM_MAGIC);

   

     /* Escribimos la hora de encendido de luz */

     FLASH_ProgramByte(EEPROM_L_ON,RTC_Light_On.RTC_Hours);

     FLASH_ProgramByte(EEPROM_L_ON+1,RTC_Light_On.RTC_Minutes);

         

     /* Escribimos la hora de apagado de luz */

     FLASH_ProgramByte(EEPROM_L_OFF,RTC_Light_Off.RTC_Hours);

     FLASH_ProgramByte(EEPROM_L_OFF+1,RTC_Light_Off.RTC_Minutes);

         

     /* Escribimos la hora de dar de comer */

     FLASH_ProgramByte(EEPROM_FEED,RTC_Feed.RTC_Hours);

     FLASH_ProgramByte(EEPROM_FEED+1,RTC_Feed.RTC_Minutes);

   

     /* Escribimos el numero de ciclos de alimentacion */

   FLASH_ProgramByte(EEPROM_NFEED,feedCycles);  

           

     LCD_GLASS_DisplayString("OK");  /* Muestra mensaje */

     delay_ms(250);

 }

/*

 Damos de comer a los peces un ciclo

*/

void Give_ONE_Feed()

 {

     LCD_GLASS_DisplayString("FEED  ");

   

     PUMP_OFF;           /* Apagamos la bomba */

   

     FEED_F;             /* Movemos direccion F */

     delay_ms(100);

     FEED_OFF;

     delay_ms(100);

   

     FEED_R;             /* Movemos direccion R */

     delay_ms(100);

     FEED_OFF;

     delay_ms(300);

   

     FEED_F;             /* Movemos direccion F */

     delay_ms(100);

     FEED_OFF;

     delay_ms(100);

 }

/*

 Damos de comer a los peces el numero de ciclos fijado

 */

void GiveFeed()

 {

     uint8_t i;

         

     for (i=0;i<feedCycles;i++)

                    {

            Give_ONE_Feed();

              }

 }

/* Funcion que cambia entre los modos manual y automatico */

void Change_Mode()

{

    if (modo==MODO_MANUAL)

              {

                            modo=MODO_AUTOMATICO;

                            BAR3_ON;

                            }

                     else

                      {

                            modo=MODO_MANUAL;

                            BAR3_OFF;

                      }

}

/* Rutina de menu */

void menu()

 {

    int8_t option; /* Opcion del menu */

    uint8_t i;            /* Bucle */

    uint8_t fdeb;   /* Flag de debounce */

   

    option=0;

    fdeb=1;

           

    for(i=0;i<20;i++)

     {

         /* Mostramos la opcion */

             switch (option)

                            {

                                    case 0: LCD_GLASS_DisplayString("MFEED");

                                                                    break;

                                    case 1: if (modo==MODO_AUTOMATICO)

                                                                                            LCD_GLASS_DisplayString("MANUAL");

                                                                                            else

                                                                                            LCD_GLASS_DisplayString("AUTO");

                                                            break;

                                    case 2: LCD_GLASS_DisplayString("T SET");

                                                                    break;

                                    case 3:    LCD_GLASS_DisplayString("L ON");

                                                                    break;                    

                                    case 4:    LCD_GLASS_DisplayString("L OFF");

                                                                    break;

                                    case 5:    LCD_GLASS_DisplayString("FEED");

                                                                    break;

                                    case 6:    LCD_GLASS_DisplayString("NFEED");

                                                                    break;                            

                                    case 7: LCD_GLASS_DisplayString("SAVE");

                                                                    break;                            

                            }    

                           

            /* Debounce if needed */

        if (fdeb)

                    {

                    Debounce(BOK_PORT,BOK_PIN);

                    Debounce(BPLUS_PORT,BPLUS_PIN);

                    Debounce(BMINUS_PORT,BMINUS_PIN);

              fdeb=0;

              }

                           

         /* Boton + */            

             if  (!(BPLUS_PORT->IDR & BPLUS_PIN))

                                            {

                                            option++;

                                            fdeb=1; /* Necesitamos debounce */

                                            i=0;        /* Reset timeout */

                                      }

                                           

            /* Boton - */                            

            if  (!(BMINUS_PORT->IDR & BMINUS_PIN))

                                      {

                                            option--;

                                            fdeb=1; /* Necesitamos debounce */

                                            i=0;        /* Reset timeout */

                                      }

                                           

            /* Verificamos rango de las opciones */

        if (option<0) option=7;

        if (option>7) option=0;            

                                           

           

            /* Boton OK */

            if  (!(BOK_PORT->IDR & BOK_PIN))

                                            {

                                            switch(option)

                                              {

                                                    case 0: /* Manual Feed */

                                                                            GiveFeed();

                          break;

                                                    case 1: /* Cambia modo manual/auto */

                                                                              Change_Mode();

                                                                break;

                                                    case 2: /* Time set */

                                                                /* Espera a la sincronizacion del calendario */

                                                                                    while (RTC_WaitForSynchro() != SUCCESS);

                                                                                    /* Lee la hora */

                                                                              RTC_GetTime(RTC_Format_BCD, &RTC_TimeStr);

                                                                                    /* Fija la nueva hora */

                                                                                    Fijar_Hora(&RTC_TimeStr);

                                                                                    /* Actualiza el RTC */

                                                                                    RTC_SetTime(RTC_Format_BCD, &RTC_TimeStr);

                                                                              break;

                                                    case 3: /* Light ON Set */

                                                                      Fijar_Hora(&RTC_Light_On);

                                                                            break;

                                                    case 4: /* Light OFF Set */

                                                              Fijar_Hora(&RTC_Light_Off);

                                                                            break;

                                                    case 5: /* Feed Time Set */

                                                              Fijar_Hora(&RTC_Feed);

                                                                            /* Verificamos que no sea despues de las 23h */

                                                                            if ((RTC_Feed.RTC_Hours)==0x23)

                                                                                           RTC_Feed.RTC_Minutes=0x00;

                                                                            break;

                                                    case 6: /* Numero de ciclos de alimentacion */

                                                                            Fijar_Ciclos_Alimentador();

                                                                      break;

                                                    case 7: /* Save to EEPROM */

                                                                      Escribir_EEPROM();

                                                                            break;

                                              }

                                                   

                                            Debounce(BOK_PORT,BOK_PIN);    

                                            return; /* Salimos de la funcion */

                                    }

             delay_ms(10); /* Test cada 1/10 s */                                    

             }

    /* Timeout, return */

 }

/*

 Inicializacion del sistema

 Realiza las siguientes acciones:

   

            Inicia los GPIOs

*/

void init_all()

 {

    /* Inicialización de GPIOs */

 

  /* Pulsador PC1: Modo de entrada flotante (ya tiene pull-up) sin interrupción */

  GPIO_Init( BOK_PORT, BOK_PIN, GPIO_Mode_In_FL_No_IT);

   

  /* Pulsador Mas: Modo de entrada con pull-up sin interrupción */

  GPIO_Init( BPLUS_PORT, BPLUS_PIN, GPIO_Mode_In_PU_No_IT);    

   

  /* Pulsador Menos: Modo de entrada con pull-up sin interrupción */

  GPIO_Init( BMINUS_PORT, BMINUS_PIN, GPIO_Mode_In_PU_No_IT);    

   

  /* Control Luz: Led Verde: Modo de Salida Push Pull (10MHz) a nivel bajo (apagado) */

  GPIO_Init( LIGHT_PORT, LIGHT_PIN, GPIO_Mode_Out_PP_Low_Fast);

   

  /* Control Bomba: Led Azul: Modo de Salida Push Pull (10MHz) a nivel alto (apagado) */

  GPIO_Init( PUMP_PORT, PUMP_PIN, GPIO_Mode_Out_PP_Low_Fast);

   

    /* Control Alimentador F: Modo de Salida Push Pull (10MHz) a nivel bajo (apagado) */

  GPIO_Init( FEED_F_PORT, FEED_F_PIN, GPIO_Mode_Out_PP_Low_Fast);

   

    /* Control Alimentador R: Modo de Salida Push Pull (10MHz) a nivel bajo (apagado) */

  GPIO_Init( FEED_R_PORT, FEED_R_PIN, GPIO_Mode_Out_PP_Low_Fast);

   

    /* Inicializamos el LCD */

  LCD_GLASS_Init();

   

  /* Habilitamos las interrupciones */

  enableInterrupts();    

  /* Iniciamos las barras laterales del LCD */

  BAR0_OFF;        /* Inferior */

  BAR1_OFF;

  BAR2_OFF;

  BAR3_OFF;   /* Superior */

  /* Mensaje de arranque */

    #ifdef SPLASH

            LCD_GLASS_ScrollSentence("               AQUARIUM AUTOMATION",1,SCROLL_SPEED);

            LCD_GLASS_DisplayString(VERSION);  /* Muestra la version */

            delay_ms(250);                            /* Retardo de estabilizacion */            

    #endif    

           

    LCD_GLASS_Clear();  /* Borramos la pantalla */

    LCD_bar();                      /* Actualizamos las barras*/

   

    /* Inicializamos el Reloj */

  LCD_GLASS_DisplayString("CLK32");  /* Muestra mensaje */

   

    /* Habilitamos el LSE */

  CLK_LSEConfig(CLK_LSE_ON);

  /* Esperamos a que el LSE esté preparado */

  while (CLK_GetFlagStatus(CLK_FLAG_LSERDY) == RESET);

  /* Esperamos a la estabilización del LSE */

  delay_ms(500);

  /* Fijamos el LSE como la fuente de reloj del RTC */

  CLK_RTCClockConfig(CLK_RTCCLKSource_LSE, CLK_RTCCLKDiv_1);

  CLK_PeripheralClockConfig(CLK_Peripheral_RTC, ENABLE);

  /* Inicializamos la hora */

    RTC_TimeStructInit(&RTC_TimeStr);

  RTC_TimeStr.RTC_Hours   = 00;

  RTC_TimeStr.RTC_Minutes = 00;

  RTC_TimeStr.RTC_Seconds = 00;

  RTC_SetTime(RTC_Format_BIN, &RTC_TimeStr);

   

    /* Inicializamos las horas de arranque y apagado de la luz */

    RTC_Light_On.RTC_Hours = 0x09;   /* 9h encendido */

    RTC_Light_On.RTC_Minutes = 0x00;

    RTC_Light_Off.RTC_Hours = 0x22;   /* 22h apagado */

    RTC_Light_Off.RTC_Minutes = 0x00;

   

    /* Iniciamos la hora de dar la comida */

    RTC_Feed.RTC_Hours = 0x18;   /* 18h dar comida */

    RTC_Feed.RTC_Minutes = 0x00;

   

    /* Borramos el feed flag */

    feedFlag=0;

   

    /* Arrancamos en modo automatico */

    modo=MODO_AUTOMATICO;

    BAR3_ON;

   

    /* Arrancamos con un unico ciclo del alimentador */

    feedCycles=1;

 }

/*

  Rutina que convierte una especificacion temporal

    en formato BCD a minutos desde la media noche.

    No se cuentan los segundos.

   

    Retorna los minutos desde la media noche.

*/

uint16_t hhmm2mm(RTC_TimeTypeDef *TimeStr)

 {

     uint16_t value;

   

     value=(((TimeStr->RTC_Hours)&0xF0)>>4)*600;

     value+=((TimeStr->RTC_Hours)&0x0F)*60;

     value+=(((TimeStr->RTC_Minutes)&0xF0)>>4)*10;

     value+=((TimeStr->RTC_Minutes)&0x0F);

   

     return value;

 }

 

 

/*

  Rutina que mira si se ha de tomar alguna accion dependiendo de la hora

   

    Enciende la luz si estamos en el rango horario en que toca

   

    Da la comida y apaga la bomba si toca.

   

    Enciende la bomba si toca.

*/

void check_times()

 {

     uint16_t l_on;

     uint16_t l_off;

     uint16_t feed;

     uint16_t time;

   

     if (modo==MODO_MANUAL) /* Miramos si estamos en modo manual */

                            {

                                    LIGHT_ON;  /* Luz encendida */

                                    PUMP_ON;   /* Bomba encendida */

                                    return; /* Salimos de la funcion */

                            }

   

     /* Actuacion en modo automatico */

   

     /* Leemos horas de encendido y apagado */

     l_on=hhmm2mm(&RTC_Light_On);

     l_off=hhmm2mm(&RTC_Light_Off);

     feed=hhmm2mm(&RTC_Feed);

   

     /* Espera a la sincronizacion del calendario */

     while (RTC_WaitForSynchro() != SUCCESS);

     /* Lee la hora */

     RTC_GetTime(RTC_Format_BCD, &RTC_TimeStr);

     /* Convierte a minutos */

     time=hhmm2mm(&RTC_TimeStr);

   

     if (l_on>l_off) /* Si la hora de encendido es despues de la de apagado */

              {

                                    if ((time>=l_on)||(time<l_off))

                                                    {LIGHT_ON;}

                                                            else

                                                    {LIGHT_OFF;}

                      }

                            else /* Si la hora de apagado es despues de la de encendido */

                            {

                                    if ((time>=l_off)||(time<l_on))

                                                    {LIGHT_OFF;}

                                                            else

                                                    {LIGHT_ON;}

                            }

                           

    /* La funcionalidad de apagado de la bomba al alimentar los peces depende de que no se */

  /* Fije la hora de alimentar los peces despues de las 23h  */

  /* Ello se ha de forzar al fijar la hora de dar de comer */

  if ((time>=feed)&&((time-feed)<PUMP_OFF_TIME))

                    {PUMP_OFF;}

                            else

                    {PUMP_ON;}

                           

    if (((time-feed)<5)&&(time>=feed)) /* Horquilla de 5 minutos para dar comida */

                             if (!feedFlag)

                                             {

                                             GiveFeed(); /* Darles de comer si no se ha hecho */

                                             feedFlag=1; /* Indicamos que se ha dado comida */

                                           }

                                           

    if (((time-feed)>5)||(time<feed)) feedFlag=0; /* Reset del flag de dar de comer */

 }

/*

 Rutina que lee en la EEPROM si hay informacion de horarios

 

 EEPROM_START  

 EEPROM_L_ON        Informacion de Hora/Min de Light ON  (2 bytes)

 EEPROM_L_OFF   Informacion de Hora/Min de Light OFF (2 bytes)

 EEPROM_FEED        Informacion de Hora/Min Feed (2 bytes)

 */

void Leer_EEPROM()

 {

     /* Desbloqueamos la memoria de Datios */

     FLASH_Unlock(FLASH_MemType_Data);

   

     /* Leemos el codigo magico */

     if (FLASH_ReadByte(EEPROM_START)!=EEPROM_MAGIC)

                            {

                                    LCD_GLASS_DisplayString("NODATA");  /* Muestra mensaje */

                                    delay_ms(250);

                                    return;

                            }

   

     /* Seguimos si se ha leido el codigo magico correcto */

   

     LCD_GLASS_DisplayString("DLOAD");  /* Muestra mensaje */

         

     /* leemos la hora de encendido de luz */

     RTC_Light_On.RTC_Hours=FLASH_ReadByte(EEPROM_L_ON);

   RTC_Light_On.RTC_Minutes=FLASH_ReadByte(EEPROM_L_ON+1);    

   

     /* Leemos la hora de apagado de luz */

     RTC_Light_Off.RTC_Hours=FLASH_ReadByte(EEPROM_L_OFF);

   RTC_Light_Off.RTC_Minutes=FLASH_ReadByte(EEPROM_L_OFF+1);

   

     /* Leemos la hora de dar de comer */

     RTC_Feed.RTC_Hours=FLASH_ReadByte(EEPROM_FEED);

   RTC_Feed.RTC_Minutes=FLASH_ReadByte(EEPROM_FEED+1);

   

     /* Leemos el numero ce ciclos de comida */

     feedCycles=FLASH_ReadByte(EEPROM_NFEED);

   

     delay_ms(250);

 }

/* Rutina principal del sistema */

void main(void)

{

  /* Inicializa el sistema */

  init_all();

  /* Miramos si hay informacion en la EEPROM */

    Leer_EEPROM();

    LCD_GLASS_Clear();  /* Borramos la pantalla */

           

    /* Encendemos la bomba */

    PUMP_ON;

   

    while (1) /* Bucle principal */

           {

             /* Saltamos a MENU si se pulsa OK */    

             if  (!(BOK_PORT->IDR & BOK_PIN)) menu();

           

             /* Mostramos la hora actual */

             Time_Show();

           

             /* Miramos si se ha de encender la luz o dar comida */

             check_times();

           

             /* Esperamos antes de repetir el bucle */

             delay_ms(10);

             }

   

    }