
#include <stdio.h>

#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"

#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/pin_map.h"
#include "driverlib/adc.h"

#define RED_LED   GPIO_PIN_1
#define BLUE_LED  GPIO_PIN_2
#define GREEN_LED GPIO_PIN_3

#define TEMP_TARGET     25
#define ERR             0.5
#define TEMP_LIMIT      TEMP_TARGET+ERR
#define AD_MAX          TEMP_TARGET/(3.3/4095)/100
#define AD_LIMIT        ERR/(3.3/4095)/100
#define INTERVAL        500


void adc_init( void );
long get_relay_time( void );
void SysTick_Handler(void);

extern void lcd_init( void );
extern void lcd_clear_display( void );
extern void lcd_set_cursor( char, char );

unsigned long   uldelay = 0;
unsigned char   stat;

int main()
{
    unsigned long      ulADC0_Value[1];
    float               voltage, temp;
    long                cnt, relay_time;
    static const char relay[2][3] = {"OFF", "ON "};

    // 動作クロックを80MHzに設定
    ROM_SysCtlClockSet( SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN );
    
    // ADCモジュールを初期化
    adc_init();

    // ポートCをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOC );
    // ポートFをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOF );
    
    // リレーを制御するポートを出力に設定
    ROM_GPIOPinTypeGPIOOutput( GPIO_PORTC_BASE, GPIO_PIN_7 );
    // LED（緑）のポートを出力に設定
    ROM_GPIOPinTypeGPIOOutput( GPIO_PORTF_BASE, GREEN_LED );
    // リレーを制御するポートにLを出力（LED点灯）
    ROM_GPIOPinWrite( GPIO_PORTC_BASE, GPIO_PIN_7, 0x00 );
    // LED（緑）にHを出力（LED点灯）
    ROM_GPIOPinWrite( GPIO_PORTF_BASE, GREEN_LED, GREEN_LED );

    // SysTickタイマーを1msecに設定
    ROM_SysTickPeriodSet( 80000 );
    // SysTickタイマーの割り込みをイネーブル
    ROM_SysTickIntEnable();
    // SysTickタイマーをイネーブル
    ROM_SysTickEnable();

    // 割り込みをイネーブル
    ROM_IntMasterEnable();    
    
    // LCDモジュールを初期化
    lcd_init();
    // LCDモジュールの表示をクリア
    lcd_clear_display();
    // LCDモジュールのカーソルを1行目の1文字目に設定
    lcd_set_cursor( 1, 1 );
    // メッセージを表示
    printf("Get Relay");
    // LCDモジュールのカーソルを2行目の8文字目に設定
    lcd_set_cursor( 2, 5 );
    // メッセージを表示
    printf("Switch Time");

    // リレー切り替え時間を取得
    relay_time = get_relay_time();

    // LCDモジュールの表示をクリア
    lcd_clear_display();
    
    // 無限ループ
    while(1)
    {
      // AD変換終了待ち
      if ( ( ROM_ADCIntStatus(ADC0_BASE, 3, false) & ADC_INT_SS3 ) == ADC_INT_SS3 ) {
        // 割り込みフラグをクリア
        ROM_ADCIntClear(ADC0_BASE, 3);

        // AD変換値を読み込み
        ROM_ADCSequenceDataGet(ADC0_BASE, 3, ulADC0_Value);

        // 電圧値を計算
        voltage = (float)(ulADC0_Value[0]) * 3.3f / 4095.0f;
        // 温度を計算
        temp = voltage * 100.0f;

        if ( cnt-- <= 0 ) {
          cnt = relay_time;
          if ( ulADC0_Value[0] > AD_MAX ) {
            // リレーをOFF
            ROM_GPIOPinWrite( GPIO_PORTC_BASE, GPIO_PIN_7, 0x00 );
            // リレーのステータスをOFFに変更
            stat = 0;
          }
          else {
            // リレーをON
            ROM_GPIOPinWrite( GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_PIN_7 );
            // リレーのステータスをONに変更
            stat = 1;
          }
        }
        // AD変換開始
        ROM_ADCProcessorTrigger(ADC0_BASE, 3);
      }
      
      // LCDモジュールのカーソルを1行目の1文字目に設定
      lcd_set_cursor( 1, 1 );
      // LCDにAD変換値と電圧を表示
      printf("AD=%3d Volt=%1.2f", ulADC0_Value[0], voltage);
      // LCDモジュールのカーソルを2行目の1文字目に設定
      lcd_set_cursor( 2, 1 );
      // LCDに温度を表示
      printf("Temp=%2.1f", temp);
      // LCDモジュールのカーソルを2行目の11文字目に設定
      lcd_set_cursor( 2, 11 );
      // LCDにリレーの状態を表示
      printf("Ry=");
      printf(relay[stat]);

      // ループのインターバルを設定
      uldelay = INTERVAL;
      while( uldelay );
    }
}

// ADCモジュール初期化関数
void adc_init( void )
{
    // ADC0モジュールをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC0 );
    // ポートEをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOE );
    // ピンタイプをADCに設定
    ROM_GPIOPinTypeADC( GPIO_PORTE_BASE, GPIO_PIN_3 );
    
    // サンプリングレートを125kspsに設定
    ROM_SysCtlADCSpeedSet( SYSCTL_ADCSPEED_125KSPS );
    // ADCシーケンサーのトリガー条件と優先順位を設定
    ROM_ADCSequenceConfigure( ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0 );
    // ADシーケンサーのステップごとの動作を設定
    ROM_ADCSequenceStepConfigure( ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END );
    // オーバーサンプリングを設定
    ROM_ADCHardwareOversampleConfigure( ADC0_BASE, 64 );
    // ADCシーケンサーをイネーブル
    ROM_ADCSequenceEnable( ADC0_BASE, 3 );
    // ADCの割り込みフラグをクリア
    ROM_ADCIntClear( ADC0_BASE, 3 );
}

// リレー切り替え時間測定関数
long get_relay_time( void )
{
    unsigned long   adc0_init, adc0;

    // AD変換開始
    ROM_ADCProcessorTrigger(ADC0_BASE, 3);

    // AD変換終了待ち
    while ( ( ROM_ADCIntStatus(ADC0_BASE, 3, false) & ADC_INT_SS3 ) != ADC_INT_SS3 );

    // 割り込みフラグをクリア
    ROM_ADCIntClear(ADC0_BASE, 3);
    // AD変換値を読み込み
    ROM_ADCSequenceDataGet(ADC0_BASE, 3, &adc0_init);
    adc0_init += AD_LIMIT;
    // AD変換開始
    ROM_ADCProcessorTrigger(ADC0_BASE, 3);

    // リレーをON
    ROM_GPIOPinWrite( GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_PIN_7 );
    // リレーのステータスをONに変更
    stat = 1;
    
    uldelay = 60000;
    do {
      // AD変換終了待ち
      while ( ( ROM_ADCIntStatus(ADC0_BASE, 3, false) & ADC_INT_SS3 ) != ADC_INT_SS3 );
      // 割り込みフラグをクリア
      ROM_ADCIntClear(ADC0_BASE, 3);
      // AD変換値を読み込み
      ROM_ADCSequenceDataGet(ADC0_BASE, 3, &adc0);
      // AD変換開始
      ROM_ADCProcessorTrigger(ADC0_BASE, 3);
    } while( adc0 <= adc0_init );

    return ( ( 60000 - uldelay ) / INTERVAL );
}

// SysTickタイマー割り込み関数
void SysTick_Handler(void) {
  static unsigned long cnt = 0;
  static long  led = 0;
  
  if ( uldelay > 0 ) uldelay--;
  
  if ( cnt >= 500 ) {
    // カウント値をリセット
    cnt = 0;
    // LEDの出力値を反転
    led = ~led;
    // LEDに値を出力
    ROM_GPIOPinWrite( GPIO_PORTF_BASE, GREEN_LED, led );
  }
  else {
     cnt++;
  }
}

