// CONFIG1
#pragma config FEXTOSC = OFF        // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT32     // Power-up default value for COSC bits (HFINTOSC with 2x PLL (32MHz))
#pragma config CLKOUTEN = OFF       // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2)
#pragma config CSWEN = ON           // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON           // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config MCLRE = OFF           // Master Clear Enable bit (MCLR/VPP pin function is MCLR; Weak pull-up enabled)
#pragma config PWRTE = ON           // Power-up Timer Enable bit (PWRT disabled)
#pragma config WDTE = OFF           // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored)
#pragma config LPBOREN = OFF        // Low-power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = ON           // Brown-out Reset Enable bits (Brown-out Reset enabled, SBOREN bit ignored)
//#pragma config BORV = LOW           // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V)
#pragma config PPS1WAY = ON         // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON          // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a Reset)
#pragma config DEBUG = OFF          // Debugger enable bit (Background debugger disabled)

// CONFIG3
#pragma config WRT = OFF            // User NVM self-write protection bits (Write protection off)
#pragma config LVP = OFF             // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored.)

// CONFIG4
#pragma config CP = OFF             // User NVM Program Memory Code Protection bit (User NVM code protection disabled)
#pragma config CPD = OFF            // Data NVM Memory Code Protection bit (Data NVM code protection disabled)

// MCLRとRE3の使い分け
// MCLR・・・MCLRE/ON   LVP/ON
// RA3 ・・・MCLRE/OFF  LVP/OFF
// このPower PackではRE3としてLEDを使用するため、MCLREとLVPは共にOFFにします

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "pic16F18855.h"
#include "Char_LCD.h"

// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ  32000000

//#define PushSW          RA4                   // ?Rev2.0
#define LED_Blue        RA4                     // Rev3.0?
#define Volume          RA5
#define Sensor_in1      RA6                     // Analog入力
#define Sensor_in2      RA7                     // Analog入力

#define Point2_out      RB0
#define Point1_out      RB1
#define FeedBack_in2    RB2                     // Analog入力
#define FeedBack_in1    RB3                     // Analog入力
#define For_SW          RB4
#define Rev_SW          RB5
#define LED_Orenge      RB6
#define LED_Green       RB7

#define LCD_BACK_Light  RC0
#define PWM2_Out        RC1
#define PWM1_Out        RC2
#define PointSW1        RC6
#define PointSW2        RC7
#define PushSW          RE3                     // Rev3.0? (?Rev2.0は未使用)

typedef unsigned char   uint8;
typedef unsigned short  uint16;
typedef unsigned long   uint32;
typedef signed   char   int8;
typedef signed   short  int16;
typedef signed   long   int32;
typedef          float  float32;

#define PWM_DELEY_TIME   3500                                          // PWMのdeley時間。3ms (周期4ms 250Hz)
//#define Dec_rate  10
//#define Acc_rate  10
//#define Stop_time 3;                                                    // Stop Time 3s

int8   Acc_rate = 10;
int8   Dec_rate = 10;
int8   Rate_Set = 10;
int16  Stop_time = 3;
int16  Stop_time_set = 3;
uint8  CW_CCW = 0;
uint8  Manu_Auto = 0;
int16  Speed = 0;
int16  StopSpeed = 0;
uint8  DriveL = 0;
uint8  DriveH = 250;
int16  FB_val = 0;
int16  ADC_FB_val = 0;
int16  ADC_VR2_in = 0;
uint8  ADC_GAIN_in = 0;                                             // FeedbackのGAIN初期値
uint8  ADC_GAIN_set = 8;                                            // FeedbackのGAIN設定値
uint8  LIGHT_ON_in = 0;                                             // LIGHT_ONの初期値
uint8  LIGHT_ON_set = 0;                                            // LIGHT_ON設定値
uint8  AutoMode_in = 0;
uint8  AutoMode_set = 0;
int8  Through = 0;
int8  Through_set;
uint16  Sen1_in = 255;
uint16  Sen2_in = 255;
uint8   Hysteresis = 15;                                             // 0.01953125V * 15 =0.29296875V
uint8   Sen1_Dig = 1;
uint8   Sen2_Dig = 1;
uint8	sen_flg = 0;
uint8   Sen1_flg = 0;
uint8   Sen2_flg = 0;
uint16  Slice_level = 205;                                           // センサーのHi/Lowスライスレベル 80%
uint8   Accel = 0;
char    buff[256];                                                  // 文字バッファ
int16   FB_chk_val = 0;
uint8   Sen2_in_min = 255 ;
uint8  i = 0;
char   auto_drive_mode = 0;                                         // 0=楕円2駅モード/1=単線往復モード
uint8  after_int = 0;
int16  GainN = 10;
uint8  Gmode = 3;
uint8  Flag_PushSW1 = 0;
uint8  Flag_PushSW2 = 0;
uint8  Flag_PushSW3 = 0;
uint16 time_dely = 0;
uint8 skip_flg = 1;

__EEPROM_DATA (0x05, 0x00, 0x03, 0x00, 0x05, 0x0A, 0x00, 0x00);     // EEPROMの初期値。8バイト単位で初期化すること。
                                                                    // 1Byte目がDownload後のGAIN値(08)
// Function
void FB_input(void);
void ADC_input(void);                                              // ACD変換入力処理
void go_wait(void);                                                // GO変化待ち
void go_wait_interrupt(void);                                      // GO変化待ち(割り込み処理用)
void GAIN_set(void);                                               // Feedback Gain設定処理
void L_on_set(void);
void PWM_out(void);                                                // PWM出力
void AUTO_Mode(void);
void Auto_drv(void);
void Sensor_on(void);
void Auto_Mode_Set(void);

// ******************************
// 割り込み処理　(この割り込み内で掛け算、割り算を使用するとワーニングがでる)
// ******************************
void __interrupt() isr(void)
{
    //INTCONbits.GIE = 0;                                         //全割り込み停止
    //Timer1の割り込みなら(10ms周期)
    if (TMR1IF == 1) {

        PIR4bits.TMR1IF = 0;                                        // タイマ1割込みフラグクリア
                
        TMR1H = 0xD8;                                               // タイマー１の再設定（65536-10000=55536)=1ms周期
        TMR1L = 0xF0;
        //TMR1IF = 0;
        PIR4bits.TMR1IF = 0;                                        // タイマ1割込みフラグクリア
        
        if(time_dely == 0){
            skip_flg = 0;                                           // センサー読み込みフラグ　読み込み
            LED_Green = 0;                                          // GreenLED点灯
        }
        else{
            skip_flg = 1;                                           // センサー読み込みフラグ　スキップ
            LED_Green = 1;                                          // GreenLED消灯
            if(time_dely > 0){
                time_dely--;
            }
        }
    }

    //Timer2の割り込みなら(8ms周期)
    else if (TMR2IF == 1) { 
        //TMR2IF = 0;
        PIR4bits.TMR2IF = 0;                                        // タイマ2割込みフラグクリア
        
                                                                    // フードバック電圧の取り込み周期調整
        if(GainN >= ADC_VR2_in){                                    // 速度が上がる毎にフィードバック取り込み周期を荒くする
            LED_Orenge = 0;                                         // タイミング確認用 check check check check check
            FB_input();                                             // FeedBack電圧の取り込み
            after_int = 1;                                          // INT Flug
            GainN = 10;
        }
        GainN = GainN + 4;                                          // 加算値によりボリューム値に依存した取り込み周期を調整
    }
    //INTCONbits.GIE = 1;                                             //全割り込み許可
}

// ******************************
//  ADC完了フラグGO変化待ち(割り込み処理用)
// ******************************
void go_wait_interrupt(void){
    __delay_us(10);                                                 // 10us Delay　(アクイジョンタイム)
    ADGO = 1;                                                       // ADC変換Start
    while( ADGO );                                                  // ADC変換終了まで待機
}

// ******************************
//  ADC完了フラグGO変化待ち
// ******************************
void go_wait(void){
    __delay_us(10);                                                 // 10us Delay　(アクイジョンタイム)
    ADGO = 1;                                                       // ADC変換Start
    while( ADGO );                                                  // ADC変換終了まで待機
}

// ******************************
// ADC FB読み込み処理(Timer2 int)　(この割り込み内で掛け算、割り算を使用するとワーニングがでる)
// ******************************
void FB_input(void){ 
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // FEED BACK読み取りタイミング待ち
    //    PWMの立下りから1500us待ってFeedback電圧を取り込むことで起電圧を測定する
    // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    __delay_us(PWM_DELEY_TIME);                                     // PWM立ち上がりからのDelay　(周期4m時は3.5msで設定中)

    // ++++++++++++++++++++++
    // Feedback電圧の取り込み
    // ++++++++++++++++++++++
    
    // 進行方向によってADの取り込みポートを変えている
    if (CW_CCW == 1) {
        ADPCH = 0b00001011;                                         // ANB3 Port Set ADC_FB_val
        ADCON0 = 0b10000000;                                        // ANB3をADC変換に設定
        go_wait_interrupt();                                        // GO変化待ち(割り込み処理用)
        ADC_FB_val = ADRESH;                                        // Feedbackの値を取り込み
    }
    else if (CW_CCW == 0) {
        ADPCH = 0b00001010;                                         // ANB2 Port Set ADC_FB_val
        ADCON0 = 0b10000000;                                        // ANB2をADC変換に設定
        go_wait_interrupt();                                        // GO変化待ち(割り込み処理用)
        ADC_FB_val = ADRESH;                                        // Feedbackの値を取り込み
    }
    if (ADC_FB_val >= 255) {                                        // PR2の最大値を超えてたら
        ADC_FB_val = 255;                                           // PR2の最大値にする
    }
    ADC_FB_val = ADC_FB_val;                                        // 取り込んだ値にGAINモードを掛ける
                                                                    // 割り込み内でGmodeを掛けるとワーニングが出るので、メイン側で処理を行う
    
    LED_Orenge = 1;                                                 // タイミング確認用
}
  
// ******************************
// ADC読み込み処理
// ******************************
void ADC_input(void){  
    // +++++++++++++++++++++++
    // 速度ボリューム値取り込み
    // +++++++++++++++++++++++
    ADPCH = 0b00000101;                                             // ANA5 Port Set ADC_VR2_in
    ADCON0 = 0b10000000;                                            // ADC Enable ; Left-justified
    go_wait();                                                      // GO変化待ち
    ADC_VR2_in = ADRESH;                                            // 速度ボリュームの値をVR2_inへ(上位8bit取り込み)
    
    // ++++++++++++++++++++++++++    
    // 位置センサー値の読み取り(1)
    // ++++++++++++++++++++++++++
    ADPCH = 0b00000110;                                             // ANA6 Port Set ADC_Sen1_in    
    ADCON0 = 0b10000000;                                            // ANA6をADC変換に設定
    go_wait();                                                      // GO変化待ち
    Sen1_in = ADRESH;                                               // センサー(1)の値をSen1_inへ
    
    // ++++++++++++++++++++++++++
    // 位置センサー値の読み取り(2)
    // ++++++++++++++++++++++++++
    ADPCH = 0b00000111;                                             // ANA7 Port Set ADC_Sen2_in 
    ADCON0 = 0b10000000;                                            // ANA7をADC変換に設定
    go_wait();                                                      // GO変化待ち
    Sen2_in = ADRESH;                                               // センサー(2)の値をSen2_inへ
    
    // +++++++++++++++++++++++
    // 位置センサーのLED表示
    // +++++++++++++++++++++++
    if (Sen1_in <= Slice_level || Sen2_in <= Slice_level) {         // センサーが検出レベルなら
        LED_Blue = 0;                                               // Blue LEDを点灯する
    }
    else {
        LED_Blue = 1;                                               // Blue LEDを消灯する
    } 
} 
   
// ******************************
// GAIN設定
// ******************************
void GAIN_set(void) {
    if( PushSW == 0 && Rev_SW == 0){                                // PUSHスイッチが押されている間待つ
        Flag_PushSW1 = 1;                                           // PUSHスイッチが押されたフラグを立てる
    }
    else{
        if(Flag_PushSW1 == 1){                                      // PUSHスイッチが離されると
            INTCONbits.GIE = 0;                                     // 全割り込み禁止
            __delay_ms(100);                                        // 100ms待機
            Flag_PushSW1 = 0;                                       // PUSHスイッチが押されているフラグはクリアー
            __delay_ms(10);                                         // 10ms待機
            LCD_Clear();                                            // LCD表示クリアー
    
            while(PushSW == 1){                                     // PUSHスイッチを押すまで待ってGAIN設定
                ADC_input();                                        // ADC読み込み
                ADC_GAIN_in = ADC_VR2_in / 26;                      // ボリューム値を1/26にした値をADC_GAIN_inに入れる(最大値9)
        
                LCD_Char_Position(0u,0u);                           // Gain設定画面表示
                LCD_Char_PrintString("FB Gain");                    //
        
                sprintf(buff,"Set%3d", ADC_GAIN_set);               // LCDにGAIN設定値を表示
                LCD_Char_Position(1u,0u);                           //
                LCD_Char_PrintString(buff);                         //
                
                sprintf(buff,"Gain%3d ", ADC_GAIN_in);              // LCDにﾎﾞﾘｭｰﾑ値0を表示
                LCD_Char_Position(1u,9u);                           //
                LCD_Char_PrintString(buff);                         //
        
                if(For_SW == 0){                                    // Gain Modeの設定(取り込んだフィードバック電圧の倍率)
                    LCD_Char_Position(0u,10);                       //
                    LCD_Char_PrintString("Slow  ");                 // SlowはGmodeを x6(低速運転向き)
                    Gmode = 6;                                      //
                }
                else if(Rev_SW == 0){                               //
                    LCD_Char_Position(0u,10);                       //
                    LCD_Char_PrintString("Core-L");                 // Core-LはGmodeを x1(コアレスモーターやHOゲージ向き)
                    Gmode = 1;                                      //
                }
                else{
                    LCD_Char_Position(0u,10);                       // 
                    LCD_Char_PrintString("Normal");                 // NolmalはGmodeを x3(Nゲージの通常モーター向き)
                    Gmode = 3;                                      //
                }
                __delay_ms(10);                                     // 10ms待機
            }
            while(PushSW == 0){                                     // Pushスイッチが押されている間ループ
            }
            
            __delay_ms(10);                                         // 10ms待機
            ADC_GAIN_set = ADC_GAIN_in;                             // Gain設定値へ
            // EEPRO書きみ込み
            eeprom_write(0, ADC_GAIN_set);                          // GainをEEPROMに書き込み
            eeprom_write(2,Gmode);                                  // Gain ModeをEEPROMに書き込み
    
            LCD_Clear();                                            // LCD表示クリアー
        
            LCD_Char_Position(0u,0u);                               //
            LCD_Char_PrintString("Please");                         // ボリュームをMinimumにするよう表示
            LCD_Char_Position(1u,2u);                               //
            LCD_Char_PrintString("Minimum Volume");                 //
        
            while(ADC_VR2_in >= 4){                                 // ボリュームがMinimumになるまでループ
                ADC_input();                                        // ADC読み込み
                __delay_ms(10);                                     // 10ms待機
            }
            while(PushSW == 0){                                     //  Pushスイッチを押している間ループ
            }
            LCD_Clear();                                            // LCD表示クリアー
            __delay_ms(10);                                         // 10ms待機
            INTCONbits.GIE = 1;                                     //全割り込み許可
        }
    }
}
// ******************************
//  常時点灯設定
// ******************************
void L_on_set(void) {
    if( PushSW == 0 && For_SW == 0){                                // PUSHスイッチが押されている間待つ
        Flag_PushSW2 = 1;                                           // PUSHスイッチが押されたフラグを立てる
    }
    else{
        if(Flag_PushSW2 == 1){
            INTCONbits.GIE = 0;                                     // 全割り込み禁止
            __delay_ms(100);                                        // 100ms待機
            Flag_PushSW2 = 0;                                       // Flag_PushSW2フラグをクリアー
            __delay_ms(10);                                         // 10ms待機
            LCD_Clear();                                            // LCD表示クリアー
    
            while( PushSW == 1 ){                                   // PUSHスイッチを押すまで待って常時点灯設定
                ADC_input();                                        // ADC読み込み
                LIGHT_ON_in = ADC_VR2_in / 10;                      // ボリューム値を1/10にした値をLIGHT_ON_inに入れる(最大値25)
        
                Speed = LIGHT_ON_in;                                // 常時点灯値をPWM値へ
                PWM_out();                                          // PWM出力
        
                LCD_Char_Position(0u,0u);                           // 常時点灯設定表示LCD出力    
                LCD_Char_PrintString("LIGHT ON Set");               //
        
                sprintf(buff,"Set%3d%% ", LIGHT_ON_set);            // LCDに常時点灯設定保存値を表示
                LCD_Char_Position(1u,0u);                           //
                LCD_Char_PrintString(buff);                         //
                
                sprintf(buff,"  LIGHT%3d%% ", LIGHT_ON_in);         // LCDにﾎﾞﾘｭｰﾑ値表示
                LCD_Char_Position(1u,6u);                           //
                LCD_Char_PrintString(buff);                         //
        
                __delay_ms(10);                                     // 10ms待機
            }
            while(PushSW == 0){                                     // Pushスイッチが押されている間ループ                             
            }                                                       //
            __delay_ms(10);                                         // 10ms待機
    
            LIGHT_ON_set = LIGHT_ON_in;                             // 常時点灯設定値へ
            // EEPRO書きみ込み
            eeprom_write(1, LIGHT_ON_set);                          // 常時点灯保存値をEEPROMに書き込み                       
    
            LCD_Clear();                                            // LCD表示クリアー
            LCD_Char_Position(0u,0u);                               //
            LCD_Char_PrintString("Please");                         // ボリュームをMinimumにするよう表示
            LCD_Char_Position(1u,2u);                               //
            LCD_Char_PrintString("Minimum Volume");                 //
        
            while(ADC_VR2_in >= 4){                                 // ボリュームがMinimumになるまでループ
                ADC_input();                                        // ADC読み込み
                __delay_ms(10);                                     // 10ms待機
            }
            while(PushSW == 0){                                     //  Pushスイッチを押している間ループ
            }
            
            LCD_Clear();                                            // LCD表示クリアー
            __delay_ms(10);                                         // 10ms待機
            INTCONbits.GIE = 1;                                     //全割り込み許可
        }
    }
}
// ******************************
//  Auto Mode設定
// ******************************
void Auto_Mode_Set(void) {
    if( PushSW == 0 && For_SW == 1 && Rev_SW == 1){                 // PUSHスイッチが押されている間待つ
        Flag_PushSW3 = 1;                                           // PUSHスイッチが押されたフラグを立てる
    }
    else{
        if(Flag_PushSW3 == 1){                                      // PUSHスイッチが押されたフラグが立っていたら
            INTCONbits.GIE = 0;                                     // 全割り込み禁止
            __delay_ms(100);                                        // 100ms待機
            Flag_PushSW3 = 0;                                       // Flag_PushSW3フラグをクリアー
            __delay_ms(10);                                         // 10ms待機
            LCD_Clear();                                            // LCD表示クリアー
    
            while( PushSW == 1 ){                                   // PUSHスイッチを押すまで待って自動モード設定
                ADC_input();                                        // ADC読み込み
                Stop_time = ADC_VR2_in / 4;                         // ボリューム値を1/4にした値をLIGHT_ON_inに入れる(最大値64)
                if(Stop_time >= 60){                                // Stop_timeが60s以上なら
                    Stop_time = 60;                                 // 60sにする
                }
       
                LCD_Char_Position(0u,0u);                           //
                LCD_Char_PrintString("Mode Set");                   // 自動運転動作モード設定表示LCD出力
                LCD_Char_Position(1u,0u);                           //
                LCD_Char_PrintString("StopTime");                   // 停車時間

                sprintf(buff,"%3ds%",Stop_time_set);                // 停車時間保存値を表示
                LCD_Char_Position(1u,8u);                           //
                LCD_Char_PrintString(buff);                         //
                
                sprintf(buff,"%3ds", Stop_time);                    // 停車時間ボリューム値表示
                LCD_Char_Position(1,12);                            //
                LCD_Char_PrintString(buff);                         //
                                        
                if(For_SW == 0){                                    // トグルスイッチがFor側なら
                    AutoMode_set = 2;                               // AutoMode_setを 2 に
                    LCD_Char_Position(0u,10u);                      //
                    LCD_Char_PrintString("Option");                 // OptionをLCDに表示
                }
                else if(Rev_SW == 0){                               // トグルスイッチがRev側なら
                    AutoMode_set = 1;                               // AutoMode_setを 1 に
                    LCD_Char_Position(0u,10u);                      //
                    LCD_Char_PrintString("GoStop");                 // GoStopをLCDに表示
                }
                else if(For_SW == 1 && Rev_SW == 1){                // トグルスイッチが中央位置なら
                    AutoMode_set = 0;                               // AutoMode_setを 0 に
                    LCD_Char_Position(0u,10u);                      //
                    LCD_Char_PrintString("GoTurn");                 // GoTurnをLCDに表示
                }
                
                __delay_ms(100);                                    // 100ms待機
            }
            while(PushSW == 0){                                     //  Pushスイッチを押している間ループ
            }
            __delay_ms(100);                                        // 100ms待機
            
            LCD_Char_Position(0u,0u);                               // 次の設定画面
            LCD_Char_PrintString("Acc/DecRate");                    // 加減速レート設定
            LCD_Char_Position(1u,0u);                               //
            LCD_Char_PrintString("Through Time  ");                  // センサー通過回数設定
            
            LCD_Char_Position(0,11);                                //
            sprintf(buff,"%2d %", Rate_Set);                        // 加減速レートの保存値を表示
            LCD_Char_PrintString(buff);                             //
            
            while(PushSW == 1){                                     // Pushスイッチが離されている間待つ
                ADC_input();                                        // ADC読み込み
                LCD_Char_Position(0u,14u);                          //
                sprintf(buff,"%2d %", ADC_VR2_in / 10);             // ボリュームの1/10を表示(Max25)
                LCD_Char_PrintString(buff);                         //
                

                if(For_SW == 0){                                    // トグルスイッチがFor側なら
                    Through_set = 2;                                // Through_setを 2 に
                }
                else if(Rev_SW == 0){                               // トグルスイッチがRev側なら
                    Through_set = 0;                                // Through_setを 0 に
                }
                else{                                               //　トグルスイッチが真ん中なら
                    Through_set = 1;                                //　Through_setを 1 に
                }
                
                LCD_Char_Position(1u,14);                           //
                sprintf(buff,"%2d %", Through_set);                 // Through_setをLCDに表示
                LCD_Char_PrintString(buff);                         //
            }
            Rate_Set = ADC_VR2_in / 10;                             // ボリューム値の1/10を加減速レートとする
            Through = Through_set;                                  // Through_setの保存値を    
            
            Stop_time_set = Stop_time;                              // 停車時間
            // EEPRO書きみ込み
            eeprom_write(3, AutoMode_set);                          // 自動運転モード保存値をEEPROMに書き込み
            eeprom_write(4, Stop_time);                             // 停車時間保存値をEEPROMに書き込み
            eeprom_write(5, Rate_Set);                              // 加減速レート保存値をEEPROMに書き込み
            eeprom_write(6,Through_set);                            // センサー通過保存値をEEPROMに書き込み
            
            LCD_Clear();                                            // LCD表示クリアー
            LCD_Char_Position(0u,0u);                               //
            LCD_Char_PrintString("Please");                         // ボリュームをMinimumにするよう表示
            LCD_Char_Position(1u,2u);                               //
            LCD_Char_PrintString("Minimum Volume");                 //
        
            while(ADC_VR2_in >= 4){                                 // ボリュームがMinimumになるまでループ
                ADC_input();                                        // ADC読み込み
                __delay_ms(10);                                     // 10ms待機
            }
            while(PushSW == 0){                                     //  Pushスイッチを押している間ループ
            }
            LCD_Clear();                                            // LCD表示クリアー
            __delay_ms(10);                                         // 10ms待機
            INTCONbits.GIE = 1;                                     //全割り込み許可
        }
    }
}

//************************************************************************
//    PORT Initialize
// ***********************************************************************
// PIC の初期化
void Initialize (void) {

    // PIC16F18855
    OSCCON1 = 0b01100000;                                           // Clock Source"HFINTOSCwith2xPLL;Clock divider"1"
    //OSCCON2 = 0b01100000;                                   　      // 32MHz
    ANSELA = 0b11100000;                                            // RA7,RA6,RA5をアナログモード、他全てのピンをデジタルモードに設定
    ANSELB = 0b00001100;                                            // RB3,RB2をアナログモード、他全てのピンをデジタルモードに設定
    ANSELC = 0b00000000;                                            // 全てのピンをデジタルモードに設定
    //ANSELE = 0b00000000;                                            // RE3をデジタルモードに設定
    
    TRISA = 0b11100000;                                             // RA7,RA6,RA5を入力、他全てのピンを出力に設定
    TRISB = 0b00111100;                                             // RB5,RB4,RB3,RB2を入力、他全てのピンを出力に設定
    TRISC = 0b11000000;                                             // RC7,RC6を入力、他全てのピンを出力に設定
    //TRISE = 0b00001000;                                             // RE3のピンを入力に設定

    WPUA  = 0b11001111;                                             // LCDデータピンと、センサー入力ピンにプルアップを付ける
    WPUB  = 0b00000000;                                             // 全てのピンにブルアップを付けない
    WPUC  = 0b00111001;                                             // 全てのピンにブルアップを付けない
    //WPUE  = 0b00000000;                                             // 全てのピンにブルアップを付けない

    PORTA = 0b00001111;                                             // 全ての出力ポートをHiにする
    PORTB = 0b11000011;                                             // 全ての出力ポートをHiにする
    PORTC = 0b00111111;                                             // 全ての出力ポートをHiにする
    //PORTE = 0b00001000;                                             // 全ての出力ポートをHiにする

    //PWM Prot SET
    PPSLOCKbits.PPSLOCKED = 0;                                      // PPS not lockd
    RC1PPS = 0b00001001;                                            // CCP1をRC1に設定(CW)
    RC2PPS = 0b00001010;                                            // CCP2をRC2に設定(CCW)
    PPSLOCKbits.PPSLOCKED = 1;                                      // PPS Lockd
    
    // Timer2 割込み周期の設定。。。
    // 1:1の場合にはPWMのON/OFFで毎回で割り込み発生
    // 1:16の場合にはPWMのON/OFFで16回に1回割り込み発生
    T2CONbits.T2OUTPS = 0b0000;                                     // Postscaler is 1:1
    //T2CONbits.T2OUTPS = 0b0001;                                     // Postscaler is 1:2
    //T2CONbits.OUTPS = 0b0011;                                       // Postscaler is 1:4
    //T2CONbits.T2OUTPS = 0b0111;                                     // Postscaler is 1:8

    // Timer2 PWM周期の設定。。。
    // 基本的には128で設定(Feedback使用時)
    // Feedbackを使用しなければ1に設定して32kHzのPWM出力が可能
    //T2CONbits.CKPS = 0b000;                                         // Prescaler is 1(32kHz)
    //T2CONbits.CKPS = 0b001;                                         // Prescaler is 2(16kHz)
    //T2CONbits.CKPS = 0b010;                                         // Prescaler is 4(8kHz)    
    //T2CONbits.CKPS = 0b011;                                         // Prescaler is 8(4kHz) フィードバックは取れない   
    //T2CONbits.CKPS = 0b100;                                         // Prescaler is 16(2kHz)高音ノイズ & フィードバック電圧取り込み時早すぎて電圧が浮いてくる    
    //T2CONbits.CKPS = 0b101;                                         // Prescaler is 32(1kHz)高音ノイズ耳障り
    //T2CONbits.CKPS = 0b110;                                         // Prescaler is 64(500Hz)若干高音ノイズ耳障り    
    T2CONbits.CKPS = 0b111;                                         // Prescaler is 128(250Hz)一番音は気にならない？
     
    PR2 = 249;                                                      // TIMER2 PERIOD REGISTER(周期)

    //　周期の計算方法　31.25KHz * Prescaler1/1 * 1/PR2(249+1) = 125Hz(8ms)
    //　周期の計算方法　8MHz * Prescaler1/128 * 1/PR2(249+1) = 250Hz(4ms)
    
    CCPR1H = DriveL;                                                // CCP1 Register High Byte(Duty)
    CCPR2H = DriveL;                                                // CCP2 Register High Byte(Duty)
        
    // Duty計算　CCPR1H / PR2 = 50 / (249+1) = 20%

    T2CLKCON = 0b0001;                                              // Clock Select Fosc/4 (8MHz) OUT Clock(250Hz - 32KHz)
    //T2CLKCON = 0b0101;                                            // Clock Select MFINTOSC 31.25KHz OUT Clock(0.9765625Hz - 125Hz)
    
    CCP1CON = 0b10011111;                                           // CCP1 Module Enable;Left aligned format;PWM mode
    CCP2CON = 0b10011111;                                           // CCP2 Module Enable;Left aligned format;PWM mode
                
    T2CONbits.ON = 1;                                               // Timer2 is on

    // Timer1割込み設定
    T1CON = 0b00110010;                                             //内部クロックの4分の１で，プリスケーラ1:8でカウント
    TMR1H = 0xD8;                                                   //タイマー１の初期化（65536-10000=55536);
    TMR1L = 0xF0;
    //TMR1IF = 0;                                                     //タイマー１割り込みフラグを０にする
    T1CLK = 0b00000001;
    T1CONbits.ON = 1;                                               // Timer1 is on
    
    //ADC CLK SET
    ADCLK = 0b00001111;                                             // Fosc/32
    
}        

// ************************************************************************ 
//                              Main Start
// ************************************************************************
int main(int argc, char** argv) {

    Initialize ();                                                  // ポート初期化

    LCD_Init ();                                                    // LCD 初期化
    LCD_Clear();                                                    // LCD 表示クリアー
    
    // EEPROM読み込み
    ADC_GAIN_set = eeprom_read(0);                                  // フィードバックゲイン保存値
    LIGHT_ON_set = eeprom_read(1);                                  // 室内灯常時点灯保存値
    Gmode        = eeprom_read(2);                                  // フィードバックゲインの倍率保存値(x1 Coreless　x3 Nolmal x6 Slow)
    AutoMode_set = eeprom_read(3);                                  // 自動運転モード保存値
    Stop_time_set= eeprom_read(4);                                  // 自動運転時停車時間保存値
    Rate_Set     = eeprom_read(5);                                  // 自動運転時加減速保存値
    Through_set  = eeprom_read(6);                                  // 自動運転時駅通過回数保存値
    
    Through = Through_set;                                          
    
    LCD_Char_Position(0u,0u);                                       //　オープニング画面
    LCD_Char_PrintString("P'sTec PowerPack");                       //
    LCD_Char_Position(1u,0u);                                       //
    LCD_Char_PrintString("PIC16F18855 R1.0");                       //
    
    __delay_ms(2000);                                               // オープニング画面表示時間
    LCD_Clear();                                                    // LCD表示クリアー
    
    //  Timer1割込み許可
    PIE4bits.TMR1IE = 1;                                            // Timer1割り込み許可

    // Timer2割込み許可
    PIE4bits.TMR2IE = 1;                                            // Timer2割り込み許可

    INTCONbits.PEIE = 1;                                            // 周辺割り込み許可

    INTCONbits.GIE = 1;                                             // グローバル割り込み許可

    while (1) {
        if (after_int == 1) {                                       // 割り込みタイミングの度にループ
            //INTCONbits.GIE = 0;
            //LED_Green = 0;
    
            GAIN_set();                                             // GAIN設定
            L_on_set();                                             // 常時点灯設定
            Auto_Mode_Set();                                        // 自動運転モード設定
            
            if (Manu_Auto == 0) {                                   // 自動運転時にスルー
                if (For_SW == 0) {                                  // スイッチが前進FORなら
                    CW_CCW = 0;                                     // 方向フラグを"0"
                }
                if (Rev_SW == 0) {                                  // スイッチが後進REVなら
                    CW_CCW = 1;                                     // 方向フラグを"1"
                }
            }
            if (For_SW == 1 && Rev_SW == 1) {                       // スイッチが停止STOPなら
                CW_CCW = 2;                                         // 方向フラグを"2"
                Manu_Auto = 0;                                      // 自動運転フラグを"0"手動にする
                sen_flg = 0;                                        //　検出センサー(sen1 sen2)のフラグをクリアー
                FB_val = 0;                                         //　フィードバック積算値をクリアー
                time_dely = 300;                                    // センサーの2度読み防止ディレイ(3s) 
            }
            
            // ************************************
            //      POINT Set
            // ***********************************
            
            if(Manu_Auto == 0){                                     // 自動運転時にはスルーする
                //POINT1
                if( PointSW1 == 1 ){ Point1_out = 1; }              // PointSW1に従ってポイント1を切り替える
                else{ Point1_out = 0; }                                    //
            
                //POINT2
                if( PointSW2 == 1 ){ Point2_out = 1; }              // PointSW2に従ってポイント2を切り替える
                else{ Point2_out = 0; }                                    //
            }
            // *********************
            // Feedback処理計算部分
            // *********************
            //
            // 8msタイマー割り込みから引き継がれる変数
            //
            // ADC_VR2_in 。。。速度ボリュームAD値 0-255
            // ADC_FB_val 。。。フィードバック電圧AD値 0-255 x Gmode
            // ADC_GAIN_set 。。。ゲイン設定値 0-32
            // Sen1_in 。。。センサー1のAD値 0-255
            // Sen2_in 。。。センサー2のAD値 0-255
    
            ADC_input();                                            // ADC読み込み
            
            if (ADC_VR2_in >= ADC_FB_val * Gmode) {                 // Feedback電圧がボリューム値より小さい時
                FB_val = FB_val + 2;                                // フィードバック値FB_valに 2 を加算
                if(FB_val >= 512){                                  // フィードバック値最大を超えたら 512とする
                    FB_val = 512;
                }
            }
            if (ADC_VR2_in < ADC_FB_val * Gmode) {                  // Feedback電圧がボリューム値より大きい時
                FB_val = FB_val - 6;                                // フィードバック値FB_valに 6 を減算
                if(FB_val <= -512){                                 // フィードバック値最小を超えたら -512とする
                    FB_val = -512;
                }
            }
            if (ADC_GAIN_set <= 0 || ADC_VR2_in <= 2) {             // Feedback無しの処理
                FB_val = 0;                                         // フィードバック値をクリアーする
                FB_chk_val = 0;                                     // フィードバック値をクリアーする
                
            }
            else{                                                   // Feedback有りの処理
                FB_chk_val = FB_val / (10 - ADC_GAIN_set);          // フィードバック値の計算処理
                if (FB_chk_val <= 1 && FB_chk_val >= -1 ) {         // 出力が小さくFBの変化が低いとフィードバックが効かなくなる時の処理
                    FB_chk_val = 1;                                 // FeedbackのGAINを含めた計算
                }
                if(FB_chk_val >= 128){                               // フィードバックの最大値が50%を超えるものは50%とする
                    FB_chk_val = 128;
                }
            }
            if(ADC_VR2_in + FB_chk_val <= LIGHT_ON_set){            // フィードバック値を加算した出力値が常時点灯値よりも小さい時は
                Speed = LIGHT_ON_set;                               // PWM出力値を常時点灯値とする
            }
            else{
                Speed = ADC_VR2_in + FB_chk_val;                    // ボリューム値とフィードバック値を加算しPWM出力へ
            }
                
            if (Speed >= 204){                                      // PWM(PR2)の最大値を超えてたら(PR2の最大値は255であるが80%に)
                Speed = 204;                                        // PWM(PR2)の最大値にする
            } 
            else if (Speed <= 0) {                                  // PR2の最小値を超えてたら
                Speed = 0;                                          // PR2の最小値にする
            }
            
            if(For_SW == 1 && Rev_SW == 1){                         // トグルスイッチが真ん中になったら
                Speed = 0;                                          // PWM出力値は 0 にする
                FB_val = 0;                                         // フィードバック値もクリアーする
            }

            // PWM出力
            PWM_out();                                              // 変数Speedの値をPWM出力
            
            if(CW_CCW != 2){                                        // スイッチがSTOPでなければ
                Sensor_on();                                        // Sensor検出
            }
        
            // LCD表示
            sprintf(buff,"Vol%3d%% ", ((ADC_VR2_in * 100) / 255));  // LCDにﾎﾞﾘｭｰﾑ値を表示
            LCD_Char_Position(0u,0u);
            LCD_Char_PrintString(buff);
            
            sprintf(buff," FB%4d%%" , ((FB_chk_val * 100) / 255));  // パワーパックの補正値の表示
            LCD_Char_Position(1u,8u);
            LCD_Char_PrintString(buff);

            sprintf(buff,"Out%3d%% " , ((Speed * 100) / 255));      // パワーパックの補正値を含む出力を表示
            LCD_Char_Position(1u,0u);
            LCD_Char_PrintString(buff);
            
            if(Manu_Auto == 0){                                     //  自動運転時にはスルーする
                LCD_Char_Position(0u,11u);                          //  LCD表示
                if(CW_CCW == 0){                                    //
                    LCD_Char_PrintString("  FOR");                  //  トグルスイッチのポジション表示
                }
                if(CW_CCW == 1){                                    //
                    LCD_Char_PrintString("  REV");                  //
                }
                else{                                               //
                    LCD_Char_PrintString(" STOP");                  //
                }
                
                if(Gmode == 1){                                     //　GainModeのLCD表示
                LCD_Char_Position(0u,9u);                           //
                LCD_Char_PrintString("C");                          //  ゲインモードを表示
                }
                else if(Gmode == 6){                                //
                    LCD_Char_Position(0u,9u);                       //
                    LCD_Char_PrintString("S");                      //
                }
                else if(Gmode == 3){                                //
                    LCD_Char_Position(0u,9u);                       //
                    LCD_Char_PrintString("N");                      //
                }
                sprintf(buff,"%1d", ADC_GAIN_set);                  // ゲイン設定を表示
                LCD_Char_Position(0u,10u);                          //
                LCD_Char_PrintString(buff);                         //
            }
            after_int = 0;                                          // 割り込み後のフラグクリアー
            //INTCONbits.GIE = 1;
        }
    }      
     return (EXIT_SUCCESS);    
}     
            
// ****************************
// PWM動作へ計算結果をPWMに出力
// *****************************
void PWM_out(void){
     
    if(CW_CCW == 0){                                                // CW方向へ動作
        CCPR1H = Speed;                                             // CCP1へADC読み込値を書き込み
        CCPR2H = DriveL;                                            // CCP2へADC読み込値を書き込み
    }
    else if(CW_CCW == 1){                                           // CCW方向へ動作
        CCPR2H = Speed;                                             // CCP2へADC読み込値を書き込み
        CCPR1H = DriveL;                                            // CCP1へADC読み込値を書き込み
    }
    else if(CW_CCW == 2){                                           // 停止処理
        CCPR1H = DriveL;                                            // CCP1へADC読み込値を書き込み
        CCPR2H = DriveL;                                            // CCP2へADC読み込値を書き込み
    }
}
 // \**/\**/\**/\**/\**/\**/\**/\**/\**/\**/\**/\**/
 // \**/\**/      Auto Drive Sensor In      \**/\**/
 // \**/\**/\**/\**/\**/\**/\**/\**/\**/ HiGuma \**/

void Sensor_on(void){                                              // 自動運転　動作
    if(skip_flg == 0){                                                                 
        if(sen_flg !=1){                                                // Sensor flgが1で無い時
            if(Sen1_in >= Slice_level + Hysteresis){                    // Sensor1がHiの時(1023=5V) 716=3.5V
                Sen1_flg = 1;                                           // Sen1_flgを1にする(Sensor1を一度読んだ事を表すフラグ)
            }
        }
        if(sen_flg !=2){                                                // Sensor flgが2で無い時
            if(Sen2_in >= Slice_level + Hysteresis){                    // Sensor2がHiの時(1023=5V) 716=3.5V
                Sen2_flg = 1;                                           // Sen1_flgを2にする(Sensor2を一度読んだ事を表すフラグ)
            }
        }
    
        if(Sen1_flg == 1){                                              // Sensor1のフラグがSetさせている。。。(センサーか゛Hiになった)
            if(Sen1_in <= Slice_level - Hysteresis){                    // Sensor1がLowの時(1023=5V) 716=3.5V 
                if(sen_flg != 1){                                       // Sensor flgが1で無い時
                    if(Through == 0){                                   // 駅をスルーするか？ 0になったら停車
                        sen_flg = 1;                                    // Sensor flgを1にする
                        Auto_drv();                                     // Auto_drivを処理する
                        Sen1_flg = 0;
                        Through = Through_set;                          // 駅停車後にスルー値を再読み込み
                    }
                    else{
                        Through--;                                      // センサー通過時減算
                        time_dely = 300;                                // センサーの2度読み防止ディレイ(3s)
                    }
                }
            }
        }
        if(Sen2_flg == 1){                                              // Sensor2のフラグがSetさせている。。。(センサーか゛Hiになった)
            if(Sen2_in <= Slice_level - Hysteresis){                    // Sensor2がLowの時(1023=5V) 716=3.5V
                if(sen_flg != 2){                                       // Sensor flgが2で無い時
                    if(Through == 0){                                   // 駅をスルーするか？ 0になったら停車
                        sen_flg = 2;                                    // Sensor flgを2にする
                        Auto_drv();                                     // Auto_drivを処理する
                        Sen2_flg = 0;                                   // Sensor2 flgをクリアーする
                        Through = Through_set;                          // 駅停車後にスルー値を再読み込み
                    }
                    else{
                        Through--;                                      // センサー通過時減算
                        time_dely = 300;                                // センサーの2度読み防止ディレイ(3s)
                    }
                }
            }
        }
    }
}

// *****************************
//          自動運転
// *****************************
void Auto_drv(void){
    if(AutoMode_set != 2){                                          // 自動モードがOptionでないとき(Optionの動作をまだ考えていないため)
        INTCONbits.GIE = 0;                                         // 自動運転中の加速、減速、停止中は全割り込み禁止
        LCD_Char_Position(0,9);                                     // カーソルを1行目12文字目
        LCD_Char_PrintString("AUTO");                               // LCD表示"AUTO"        
  
        Manu_Auto = 1;                                              // auto drive flgをsetする

        LCD_Char_Position(0,13);                                    // カーソルを1行目8文字目
        if(sen_flg == 0){                                           // センサー未検出時
            LCD_Char_PrintString("   ");                            // LCD表示クリアー
        }
        if(sen_flg == 1){                                           // センサー1検出時
            LCD_Char_PrintString(" S1");                            // LCD表示"S1"
        }
        if(sen_flg == 2){                                           // センサー2検出時
            LCD_Char_PrintString(" S2");                            // LCD表示"S2"
        }
    
        while(Speed >= LIGHT_ON_set){                               // PWM出力値が常点灯値より大きい場合
            PWM_out();                                              // PWM出力

            // 減速処理
            Speed--;                                                // PWM出力値を-1する
            FB_val = 0;                                             // フィードバック値をクリアーする
            FB_chk_val = 0;                                         // フィードバック値をクリアーする

            LCD_Char_Position(1,3);                                 // カーソルを2行目2文字目
            sprintf(buff, "%3d", (Speed * 100) / 255);              // PWM出力値
            LCD_Char_PrintString(buff);                             // 表示

            LCD_Char_Position(1,12);                                // カーソルを2行目11文字目
            sprintf(buff, "%4d", (FB_chk_val * 100) / 255);         // フィードバック値
            LCD_Char_PrintString(buff);                             // LCD表示

            Dec_rate = Rate_Set * 2;                                // 減速レートを指定(倍率を変更すれば減速時のみのレート変更可)
            while(Dec_rate >= 0){
            __delay_ms(1);                                          // ディレイ減速時間
            Dec_rate--;
            }
        }
                            
        Point1_out = !Point1_out;                                   // POINT1の反転
        //Point2_out = !Point2_out;                               // Point2の反転
        
        // 停車中処理
        Stop_time = Stop_time_set * 10;                             // 設定値を変数Stop_timeに
        while(Stop_time >= 0){                                      // Stop_timeが0になったらループおわり
            __delay_ms(100);                                        // 100ms待機
            
            ADC_input();                                            // ADC読み込み
            LCD_Char_Position(0,3);                                 // カーソルを1行目2文字目
            sprintf(buff, "%3d", (ADC_VR2_in * 100) / 255);         // Volume値
            LCD_Char_PrintString(buff);                             // 表示
                              
            if(For_SW == 1 && Rev_SW == 1){                         // トグルスイッチがStopになった時
                Stop_time = 0;                                      // 変数Stop_timeを 0 にする
            }
            Stop_time--;                                            // 変数Stop_timeを減算
        }
        if(For_SW == 0 || Rev_SW == 0){                             // トルスイッチがStopなら以下処理ジャンプ
                        
            // 停車中
            if(AutoMode_set == 0){                                  // 自動運手モードが 0 なら
                CW_CCW = !CW_CCW;                                   // 進行方向反転
            }
           
            // ポイント切り替え
            //Point1_out = !Point1_out;                               // POINT1の反転
            Point2_out = !Point2_out;                               // Point2の反転

            __delay_ms(1);                                          // 1ms待機(ビット操作が連続すると先の操作が動かなくなる)
            LED_Green = 1;                                          // センサースキップ表示
            
            // 発進加速処理
            Accel = LIGHT_ON_set;                                   // 加速初期値を設定
            
            while(ADC_VR2_in > Accel){                              // ボリューム値が加速初期値より小さい時にループ
                Speed = Accel;                                      // 停車出力をSpeed変数に
                PWM_out();                                          // PWM出力
                Accel++;                                            // Speed変数を加算

                LCD_Char_Position(0,3);                             // カーソルを1行目2文字目
                sprintf(buff, "%3d", (ADC_VR2_in * 100) / 255);     // Volume値
                LCD_Char_PrintString(buff);                         // 表示

                LCD_Char_Position(1,3);                             // カーソルを2行目4文字目
                sprintf(buff, "%3d", (Speed * 100) / 255);          // PWM出力値を編集
                LCD_Char_PrintString(buff);                         // 表示

                LCD_Char_Position(1,12);                            // カーソルを2行目11文字目
                sprintf(buff, "%4d", (FB_chk_val * 100) / 255);     // フィードバック値を編集
                LCD_Char_PrintString(buff);                         // LCD表示
       
                Acc_rate = Rate_Set * 8;                            // 加速レートを指定(倍率を変更すれば加速時のみのレート変更可)
                while(Acc_rate >= 0){                               // 加速レートが 0 になるまで待機
                __delay_ms(1);                                      // ディレイ減速時間
                Acc_rate--;                                         // 加速レート減算
                }
            }
        }
        time_dely = 600;                                            // センサーの2度読み防止ディレイ(6s) 
        INTCONbits.GIE = 1;                                         // 全割り込み許可
    }
}