/*!
 * \file hal.c
 *
 * Author: Christos Choutouridis AEM: 8997
 * email : <cchoutou@ece.auth.gr>
 *
 */
#include "hal.h"


/*
 * Public data types / classes
 */
alcd_t     alcd;
ow_uart_t  ow;

proximity_t    prox;

/*!
 * Initialize all hardware
 */
int hal_hw_init (void) {
   // Init base board
   NUCLEO_Init(1000);
   SHIELD_Init ();

   // Start Jiffy functionality
   jf_link_setfreq (JF_setfreq);
   jf_link_value ((jiffy_t*)&JF_TIM_VALUE);
   jf_init (JF_TIM_CLOCK_FREQ, 1000);

   return 0;
}

/*
 * ========== LCD ===========
 */
/*!
 * \brief
 *    Initialize the lcd data. Not the LCD module
 */
void lcd_init (void) {
   alcd_link_db4 (&alcd, SHIELD_LCD_DB4);
   alcd_link_db5 (&alcd, SHIELD_LCD_DB5);
   alcd_link_db6 (&alcd, SHIELD_LCD_DB6);
   alcd_link_db7 (&alcd, SHIELD_LCD_DB7);
   alcd_link_rs (&alcd, SHIELD_LCD_RS);
   alcd_link_en (&alcd, SHIELD_LCD_EN);
   alcd_link_bl (&alcd, SHIELD_LCD_BL);

   alcd_set_lines (&alcd, 2);
   alcd_set_columns (&alcd, 16);

   alcd_init (&alcd, (alcd_funset_en)LCD_FUNSET_2L8);
}

//! lcd enable wrapper
__INLINE void lcd_enable (uint8_t en) {
   alcd_enable(&alcd, en);
}

//!< lcd putchar wrapper
__INLINE int lcd_putchar (char c) {
   return alcd_putchar(&alcd, c);
}

//! lcd puts wrapper
int lcd_puts (const char *s) {
   int i =0;
   while (alcd_putchar(&alcd, *s++))
      ++i;
   return i;
}

/*
 * ========== Led interface ==========
 */
__INLINE void led_red (uint8_t en) { LED_RED (en); }
__INLINE void led_green (uint8_t en) { LED_GREEN (en); }

/*
 * ========= Relay ===========
 */
/*!
 * Create pulses to re-state reay coil
 */
void relay (uint8_t on) {
   switch (on) {
      case 0:
         SHIELD_BR1(1);
         HAL_Delay(100);
         SHIELD_BR1(0);
         break;
      default:
         SHIELD_BR2(1);
         HAL_Delay(100);
         SHIELD_BR2(0);
         break;
   }
}


/*
 * ========= Temperature ===========
 */
uint8_t ds18b20_rom[8];
uint8_t zero_rom[8] = {0};

/*!
 * Initialize temperature measurement system
 */
int temp_init (uint8_t resolution) {
   if (!IS_TEMP_RESOLUTION(resolution))
      return 1;

   // link with hardware
   ow_uart_link_rw (&ow, (ow_uart_rw_ft)SHIELD_1W_RW);
   ow_uart_link_br (&ow, (ow_uart_br_ft)SHIELD_1W_UART_BR);
   ow_uart_set_timing (&ow, OW_UART_T_STANDARD);

   ow_uart_init (&ow);              // init
   ow_uart_search(&ow, ds18b20_rom);// do a search

   if (!memcmp ((const void*)ds18b20_rom, (const void*)zero_rom, 8))
      return 1;                     // if there is no DS18b20 error

   // set resolution to 9-bit
   ow_uart_reset(&ow);
   ow_uart_tx (&ow, SKIPROM);       // Skip rom
   ow_uart_tx (&ow, WRITESCRATCH);  // write scratchpad
   ow_uart_tx (&ow, (byte_t)127);   // Th
   ow_uart_tx (&ow, (byte_t)-128);  // Tl
   ow_uart_tx (&ow, resolution);    // Configuration 9 bit
   HAL_Delay(100);
   return 0;
}

/*!
 * Temperature read functionality
 */
float temp_read (void) {
   uint8_t t[2];

   ow_uart_reset(&ow);
   ow_uart_tx (&ow, SKIPROM);       // Skip rom
   ow_uart_tx (&ow, STARTCONV);     // convert temperature
   while (ow_uart_rx(&ow) == 0)     // Wait for slave to free the bus
      ;
   //HAL_Delay (100);
   ow_uart_reset(&ow);
   ow_uart_tx (&ow, SKIPROM);       // Skip rom
   ow_uart_tx (&ow, READSCRATCH);   // read scratchpad

   t[0] = ow_uart_rx(&ow);          // LSB
   t[1] = ow_uart_rx(&ow);          // MSB
   ow_uart_reset(&ow);

   t[1] <<= 4;
   t[1] |= (t[0] >> 4);
   t[0] &= 0x0F;
   return t[1] + t[0]/16.0;
}

/*
 * ========= Proximity ===========
 */
/*!
 * Initialize proximity system
 * \param p    Which proximity object to initialize
 */
void proximity_init (proximity_t* p){
   for (int i =0 ; i<PROX_READINGS ; ++i)
      proximity(p);
}

/*!
 * Read proximity value in [cm]
 * \note
 *    This function also implements an embedded averaging filter
 * @param p    Which proximity object to use
 * @return     The measured distance
 */
float_t proximity (proximity_t* p){
   float_t ret;
   clock_t t1, t2, mark;

   SHIELD_TRIG(1);      // send pulse and mark cycles
   jf_delay_us(10);
   SHIELD_TRIG(0);
   // wait for response with timeout
   mark = clock();
   do {
      t1 = CYCLE_Get();
      if (clock() - mark >= PROX_TIME_MAX)
         return -1;
   } while (!SHIELD_ECHO());

   mark = clock();
   do {
      t2 = CYCLE_Get();
      if (clock() - mark >= PROX_TIME_MAX)
         return -1;
   } while  (SHIELD_ECHO());

   // Calculate distance
   SystemCoreClockUpdate();
   uint32_t c_usec = SystemCoreClock / 1000000;
   // Load value
   p->readings[p->iter++] = (c_usec) ? ((t2 - t1)/c_usec) / 58.2 : PROX_MAX_DISTANSE;
   p->iter %= PROX_READINGS;

   // Return filtered distance (moving average filter FIR)
   ret =0;
   for (int i=0 ; i<PROX_READINGS ; ++i)
      ret += p->readings[i];
   return ret/PROX_READINGS;
}




