#include <stdio.h>
#include <stdlib.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"
#include "driverlib/timer.h"

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


void adc_init( void );
void timer1_init( 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;
unsigned long ulADC0_Value[1], ulADC1_Value[1];
  
    
int main()
{
    long                diff, param, stat = 0;
    // モーターの動作判定パラメータ　　低電圧での閾値、中電圧での閾値、高電圧での閾値、一致判定の許容差
    static const long thresh[3][4] = { 15, 20, 50, 10,
                                          25, 50, 100, 20,
                                          40, 200, 300, 30 };
  
    // 動作クロックを80MHzに設定
    ROM_SysCtlClockSet( SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN );

    // ADCモジュールを初期化
    adc_init();
    
    // TIMER1モジュールを初期化
    timer1_init();

    // ポートFをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOF );
    
    ROM_GPIOPinTypeGPIOOutput( GPIO_PORTB_BASE, GPIO_PIN_1 | GPIO_PIN_0 );
    // LED（緑）のポートを出力に設定
    ROM_GPIOPinTypeGPIOOutput( GPIO_PORTF_BASE, GREEN_LED );
    ROM_GPIOPinWrite( GPIO_PORTB_BASE, GPIO_PIN_1 | GPIO_PIN_0, 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();

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

    // 無限ループ
    while(1)
    {
      // ループインターバル時間設定
      uldelay = 3;

      // AD0変換終了待ち
      while ( ( 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);
      // AD変換開始
      ROM_ADCProcessorTrigger(ADC0_BASE, 3);

      // AD1変換終了待ち
      while ( ( ROM_ADCIntStatus(ADC1_BASE, 3, false) & ADC_INT_SS3 ) != ADC_INT_SS3 );
      // 割り込みフラグをクリア
      ROM_ADCIntClear(ADC1_BASE, 3);
      // AD変換値を読み込み
      ROM_ADCSequenceDataGet(ADC1_BASE, 3, ulADC1_Value);
      // AD変換開始
      ROM_ADCProcessorTrigger(ADC1_BASE, 3);
      
      // ボリューム値からモーターフェーダーのパラメータを切り替え
      if ( ulADC0_Value[0] < 80 ) {
        // フェーダー位置2%まで
        param = 0;
      }
      else if ( ulADC0_Value[0] < 800 ) {
        // フェーダー位置20%まで
        param = 1;
      }
      else {
        // フェーダー位置20%以上
        param = 2;
      }
      
      // ボリュームとモーターフェーダーの位置の差を計算
      diff = abs( ulADC0_Value[0] - ulADC1_Value[0] );

      // TIMER1の動作停止
      ROM_TimerDisable( TIMER1_BASE, TIMER_B );

      // ボリュームとモーターフェーダーの位置の差からデューティを決定
      if ( diff < thresh[param][0] ) {
        // Timer1のマッチ値を設定（PWMのデューティ:40%）
        ROM_TimerMatchSet( TIMER1_BASE, TIMER_B, 0x0C80 );
      }
      else if ( diff < thresh[param][1] ) {
        // Timer1のマッチ値を設定（PWMのデューティ:60%）
        ROM_TimerMatchSet( TIMER1_BASE, TIMER_B, 0x12C0 );
      }
      else if ( diff < thresh[param][2] ) {
        // Timer1のマッチ値を設定（PWMのデューティ:80%）
        ROM_TimerMatchSet( TIMER1_BASE, TIMER_B, 0x1900 );
      }
      else {
        // Timer1のマッチ値を設定（PWMのデューティ:100%）
        ROM_TimerMatchSet( TIMER1_BASE, TIMER_B, 0x1F3F );
      }
        
      // ボリュームとモーターフェーダーの位置関係から移動方向を決定
      if ( ulADC0_Value[0] > ulADC1_Value[0] + thresh[param][3] ) {
        // 移動方向を確認
        if ( stat == 2 ) {
          // 逆方向だったら1us以上ウェイト
          ROM_SysCtlDelay( 27 );
        }
        // TIMER1の動作開始
        ROM_TimerEnable( TIMER1_BASE, TIMER_B );
        // フェーダーをモーターとは逆方向に移動
        ROM_GPIOPinWrite( GPIO_PORTB_BASE, GPIO_PIN_1 | GPIO_PIN_0, 0x01 );
        // 移動方向を代入
        stat = 1;
      }
      else if ( ulADC0_Value[0] < ulADC1_Value[0] - thresh[param][3] ) {
        // 移動方向を確認
        if ( stat == 1 ) {
          // 逆方向だったら1us以上ウェイト
          ROM_SysCtlDelay( 27 );
        }
        // TIMER1の動作開始
        ROM_TimerEnable( TIMER1_BASE, TIMER_B );
        // フェーダーをモーターの方向に移動
        ROM_GPIOPinWrite( GPIO_PORTB_BASE, GPIO_PIN_1 | GPIO_PIN_0, 0x02 );
        // 移動方向を代入
        stat = 2;
      }
      else {
        // フェーダー停止
        ROM_GPIOPinWrite( GPIO_PORTB_BASE, GPIO_PIN_1 | GPIO_PIN_0, 0x00 );
        // TIMER1の動作停止
        ROM_TimerDisable( TIMER1_BASE, TIMER_B );
        // 移動方向を代入
        stat = 0;
      }
      // インターバル時間待ち
      while( uldelay );
    }

}

// ADCモジュール初期化関数
void adc_init( void )
{
    // ADC0モジュールをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC0 );
    // ADC1モジュールをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC1 );

    // ポートEをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOE );
    // ピンタイプをADCに設定
    ROM_GPIOPinTypeADC( GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 );
    
    // ADCシーケンサーのトリガー条件と優先順位を設定
    ROM_ADCSequenceConfigure( ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0 );
    ROM_ADCSequenceConfigure( ADC1_BASE, 3, ADC_TRIGGER_PROCESSOR, 0 );
    // ADシーケンサーのステップごとの動作を設定
    ROM_ADCSequenceStepConfigure( ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END );
    ROM_ADCSequenceStepConfigure( ADC1_BASE, 3, 0, ADC_CTL_CH1 | ADC_CTL_IE | ADC_CTL_END );
    // ADCシーケンサーをイネーブル
    ROM_ADCSequenceEnable( ADC0_BASE, 3 );
    ROM_ADCSequenceEnable( ADC1_BASE, 3 );
    // ADCの割り込みフラグをクリア
    ROM_ADCIntClear( ADC0_BASE, 3 );
    ROM_ADCIntClear( ADC1_BASE, 3 );
}

// TIMER1モジュール初期化関数
void timer1_init( void )
{
    // TIMER1モジュールを初期化
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER1 );

    // ポートBをイネーブル
    ROM_SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOB );

    // 使用するピンをTimer信号に設定
    ROM_GPIOPinConfigure( GPIO_PB5_T1CCP1 );

    // ピンタイプをTimer信号に設定
    ROM_GPIOPinTypeTimer( GPIO_PORTB_BASE, GPIO_PIN_5 );

    // Timer1を16bit Timer、PWM出力動作に設定
    ROM_TimerConfigure( TIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM );
    // Timer1の出力をアクティブローに設定
    ROM_TimerControlLevel( TIMER1_BASE, TIMER_B, true );
    // Timer1のロード値を設定（PWMの周波数値:10kHz）
    ROM_TimerLoadSet( TIMER1_BASE, TIMER_B, 0x1F40 );
    // Timer1のプリスケーラ値を設定（PWMの周波数値）
    ROM_TimerPrescaleSet( TIMER1_BASE, TIMER_B, 0x00 );
    // Timer1のマッチ値を設定（PWMのデューティ:100%）
    ROM_TimerMatchSet( TIMER1_BASE, TIMER_B, 0x1F3F );
    // Timer1のプリスケーラのマッチ値を設定（PWMのデューティ）
    ROM_TimerPrescaleMatchSet( TIMER1_BASE, TIMER_B, 0x00 );
    // Timer1をイネーブル
    ROM_TimerEnable( TIMER1_BASE, TIMER_B );
}

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 );

    // Display clear
    lcd_clear_display();
    // LCDモジュールのカーソルを1行目の1文字目に設定
    lcd_set_cursor( 1, 1 );
    // LCDにAD変換値を表示
    printf("AD0=%4d", ulADC0_Value[0]);
    // LCDモジュールのカーソルを2行目の1文字目に設定
    lcd_set_cursor( 2, 1 );
    // LCDにAD変換値を表示
    printf("AD1=%4d", ulADC1_Value[0]);
  }
  else {
     cnt++;
  }
}

