/*************************************
 * File:18326_Logiana_22   =  =
 * 5 Ch ロジックツール Sampl rateはmax 10 us」
 * System ClockはConfigで内部32MHzに設定
 * PIC16F18326
 * Serial port change to RA4:Rx   RA5:Tx
 * use CLC & RESET instruction
 * CLC1 out feed to CLC2 OK
 * Created on 2021-10-02
 **************************************/

 #include <xc.h>
 #include <stdio.h>
 #include <stdbool.h>
#include <string.h>

#define _XTAL_FREQ 32000000      // delay_ms(x) のための定義

#pragma config FEXTOSC = OFF,RSTOSC = HFINT32  // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = OFF,FCMEN = OFF
#pragma config MCLRE = ON,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = ON,CP = OFF,CPD = OFF

//#define pushSW  PORTCbits.RC4

// ================= 共通変数 ====================================
bool Err_Cmd = false;         // コマンドがエラー時のフラグ
bool isRxReady = false;       // コマンド受信したフラグ
char PortData;                // 入力ポートデータ
char ChckData;                // トリガ検査データ
char AqData[1200];            // 収集データバッファ
char RxWptr = 0;              // 受信書込みポインタのリセット
char RxRptr = 0;              // 受信読込みポインタのリセット
char RxWbuf[20],RxRbuf[20];   // UART Rxバッファ
char MskPtn;                  // トリガマスクパターン
char PrePtn;                  // トリガ準備パターン
char TrgPtn;                  // トリガパターン
char TrgChn;                  // トリガChannel

int AqSat;                    // 収集開始
int AqEnd;                    // 収集終了
int AqPtr;                    // 収集ポインタ
int RcdNo;                    // 記録データ数

// ================= プロトタイプ ====================================
void ckCommand(void);
void Aquition(void);
void outAqData(char *,int);
void initClcs(void);

// ================= printf関数を使用するための定義 =================
void putch(unsigned char ch) {
    while (!TXIF);              //送信終了待ち
    TX1REG = ch;
}

// ******************* main *************************************
void main() {

    TRISA  = 0b111111;          // Port すべて入力
    TRISC  = 0b111111;          // Port すべて入力
    ANSELA = 0;                 // すべてデジタル
    ANSELC = 0;                 // すべてデジタル
    WPUA   = 0b001111;          // RA0-RA3 弱プルアップ ON
    WPUC   = 0b100000;          // RC5 弱プルアップ ON
    RXPPS  = 0x04;              //RA4->EUSART:RX
    RA5PPS = 0x14;              //RA5->EUSART:TX

//------------ Initialize timer 0 -------------------------------
    T0CON1 = 0b01000000;    // Fosc/4 Sync PreS 1/1
    T0CON0 = 0b00000000;    // T0 OFF 8bit Post 1/1

//------------ Initialize UART ----------------------------------
    RC1STA   = 0b10010000;  // 8 bit 連続受信
    TX1STA   = 0b00100100;  // 非同期送受信 High Baud Rate選択
    BAUD1CON = 0b00001000;  // 115200 baud
    SP1BRGH  = 0;
    SP1BRG   = 68;

    initClcs();

    RCIE = 1;               // UART Rx 割込みを使用許可
    PEIE = 1;               // 周辺機能割込みの使用許可
    GIE  = 1;               // 全割込みの使用許可

    printf("!!\r\n");       // 準備完了を送信
    // ----------- 繰返し --------------------------------------------
    while(1){
        while(!RxRptr);          // コマンド受信待ち
        ckCommand();             // コマンドの処理
        RxRptr = 0;              //   受信処理終了を明示
        if(Err_Cmd){             //   処理エラーがあった場合
            printf("!\r\n");     //     再送要求を送信
            continue;            //     受信待ちに戻る
        }else{                   //   正常終了
            printf("#\r\n");     //     正常終了を送信
        }
                                 // -----------データの収集-----------
        Aquition();              // ロジックデータの収集
                                 // -----------データ送信-------------
        outAqData(AqData+AqPtr, 1100-AqPtr);   // 収集
        outAqData(AqData, AqPtr);              // 収集データ送信
        printf("##\r\n");                      // データ終了を送信
    }
}

//===========================
void initClcs(){
    CLCIN0PPS = 0x10;   //RC0->CLC2:CLCIN0;
    CLCIN1PPS = 0x11;   //RC1->CLC2:CLCIN1;
    CLCIN2PPS = 0x12;   //RC2->CLC2:CLCIN2;

// Set the CLC1
    CLC1POL  = 0x02;    // G2出力は負極性; 他出力は正極性;
    CLC1SEL0 = 0x00;    // LC1D1S (CLCIN0PPS);
    CLC1SEL1 = 0x01;    // LC1D2S (CLCIN1PPS);
    CLC1SEL2 = 0x02;    // LC1D3S (CLCIN2PPS);
    CLC1SEL3 = 0x18;    // LC1D4S T0_overflow;
    CLC1GLS0 = 0x80;    // G1D4 正接続; G1D3-1 Vss;
//    CLC1GLS1 = 0x02;    // G2D4-2 Vss; G2D1 正接続;
    CLC1GLS1 = 0x01;    // G2D4-2 Vss; G2D1 正接続;
    CLC1GLS2 = 0x00;    // 全てVss;
    CLC1GLS3 = 0x00;    // 全てVss;
    CLC1CON  = 0x84;    // EN ○; INT X-X; 1-input D ff;

// Set the CLC2
    CLC2POL  = 0x02;
    CLC2SEL0 = 0x00;
    CLC2SEL1 = 0x04;
    CLC2SEL2 = 0x02;
    CLC2SEL3 = 0x18;
    CLC2GLS0 = 0x80;
//    CLC2GLS1 = 0x05;
    CLC2GLS1 = 0x06;
    CLC2GLS2 = 0x00;
    CLC2GLS3 = 0x00;
    CLC2CON  = 0x84;
}

/* ********************************************************************
 * コマンドの解釈・処理
 * *******************************************************************/
void ckCommand(void){
    int i;
    Err_Cmd = true;                  // 受信文字列のエラー確認
    if(RxRptr != 7)return;           // 受信文字数の確認
    if(RxRbuf[0] != '#')return;      // コマンド分離の確認
    if(RxRbuf[4] != '#')return;      // コマンド分離の確認
    Err_Cmd = false;

    // ----------- 各ChのTrigger条件設定 -------------------------
    // 10 = 信号源4を入力(正論理) 01 = 信号源4を反転入力
    MskPtn=0;PrePtn=0;TrgPtn=0;      // トリガパターン クリア

    for(i=3; i>0; i--){              // Ch3 -> Ch1を処理
        PrePtn<<=2;
        switch(RxRbuf[i]){
            case '0':break;                    // * 任意
            case '1':PrePtn+=1;         ;break; // 1 Hi
            case '2':PrePtn+=2;         ;break; // 0 Low
            case '3':PrePtn+=2;TrgPtn=5;TrgChn=i-1;break; // / 立上り
            case '4':PrePtn+=1;TrgPtn=6;TrgChn=i-1;break; // \ 立下り
            default:Err_Cmd = true;return;             // 指定文字以外
        }
    }
    CLC1GLS1 = PrePtn;        // CLC1に設定
    CLC2GLS1 = TrgPtn;        // CLC2に設定
    CLC2SEL0 = TrgChn;
    // ------------------ Sample Rateの設定 ---------------------
    switch(RxRbuf[5]){
        case '0':T0CON1bits.T0CKPS=0;TMR0H=  7;break;    //   1us
        case '1':T0CON1bits.T0CKPS=0;TMR0H= 15;break;    //   2us
        case '2':T0CON1bits.T0CKPS=0;TMR0H= 39;break;    //   5us
        case '3':T0CON1bits.T0CKPS=3;TMR0H=  9;break;    //  10us
        case '4':T0CON1bits.T0CKPS=3;TMR0H= 19;break;    //  20us
        case '5':T0CON1bits.T0CKPS=3;TMR0H= 49;break;    //  50us
        case '6':T0CON1bits.T0CKPS=3;TMR0H= 99;break;    // 100us
        case '7':T0CON1bits.T0CKPS=3;TMR0H=199;break;    // 200us
        case '8':T0CON1bits.T0CKPS=5;TMR0H=124;break;    // 500us
        case '9':T0CON1bits.T0CKPS=5;TMR0H=249;break;    //   1ms
//        case 'A':T0CON1bits.T0CKPS=3;TMR0H=9;break;    //   2ms
//        case 'B':T0CON1bits.T0CKPS=3;TMR0H=9;break;    //   5ms
//        case 'C':T0CON1bits.T0CKPS=3;TMR0H=9;break;    //  10ms
        default:Err_Cmd = 1;return;       // エラー 指定文字以外
    }

    // ------------------- 収集開始および終了の指定 --------------------
    switch(RxRbuf[6]){
        case '0':AqSat= 100;AqEnd=1000;break;    // トリガは前方
        case '1':AqSat=   0;AqEnd=2000;break;    // トリガから1ウインド後
        case '2':AqSat=   0;AqEnd=3000;break;    // トリガから2ウインド後
        case '3':AqSat=1100;AqEnd=   0;break;    // フリーラン
        case '4':AqSat= 550;AqEnd= 550;break;    // トリガは中央
        case '5':AqSat=1000;AqEnd= 100;break;    // トリガは後方
        default:Err_Cmd = true;return;           // エラー 指定文字以外
    }
}

/* ********************************************************************
 * 入力データの取り込み処理
 * *******************************************************************/
void Aquition(void){
    T0CON0bits.T0EN = 1;                   // Timer1スタート
    AqPtr = 0;                             // 収集ポインタ リセット

    // -------------- トリガ前保存 および FreeRun --------------
    while(AqSat--){                     // AqSat(取得開始)数まで保存
        while(!TMR0IF);                 // 取得タイミングを待つ
        TMR0IF = 0;                     //
        AqData[AqPtr++] = PORTC;        // 保存
        if(AqPtr >= 1100) AqPtr = 0;    // 保存ポインタを循環
    }
    if(AqEnd==0)return;                 // FreeRun時は、保存完了

    do{ // ---------------- トリガ条件の確認 -------------------------
             while(!TMR0IF);               // 取得タイミングを待つ
             TMR0IF = 0;
             AqData[AqPtr++] = PORTC;      // 保存
             if(AqPtr >= 1100) AqPtr = 0;
      }while(!LC2OUT);                     // トリガ前条件成立まで待つ

    // ------------------- トリガ後保存 ----------------------------
    while(AqEnd--){                     // AqEnd(取得完了)数まで保存
        while(!TMR0IF);                 // 取得タイミングを待つ
        TMR0IF = 0;                     //
        AqData[AqPtr++] = PORTC;        // 保存
        if(AqPtr >= 1100) AqPtr = 0;    // 保存ポインタを循環
    }
    T0CON0bits.T0EN = 0;                 // Timer1停止
}

/* ********************************************************************
 * Data配列送信
 * 配列のデータを送信する  一行50文字+CRLF
 * adata:Data配列名  ptr:データ数
 * ch1_pos;データのあるbit位置
 * *******************************************************************/
void outAqData(char *adata, int ptr){
    int i = 0;
    char c;
    while(ptr--){                      // 指示文字数分繰り返す
        c = *adata++;                  // データを取り出しポインタを進める
        c = c & 0x1F;                   // 下位5ビットを取り出す
        c = c | '@';                   // 「@」からの文字コードに変換
        putch(c);                      // 一文字送信
//        while(U1STAbits.UTXBF);      // 送信バッファが空なら
//        U1TXREG = c;                 // 一文字送信
        if((++i >= 50) | (ptr == 0)){  // 50文字毎または
            i = 0;                     // 指定数送信完了で
            printf("\r\n");            // CRLFを挿入
        }
    }
}

/* ********************************************************************
 * Uart1 Rx割込み処理
 * RxWptr,RxRptr; RxWbuf[20],RxRbuf[20];
 * *******************************************************************/
void __interrupt() Rx_isr(void) {
    char RxData;
    if(RCIF){
        if(RC1STAbits.OERR){                // 受信エラーなら
            RC1STAbits.CREN = 0;            //    エラークリア
            RC1STAbits.CREN = 1;            //    エラークリア
            RxWptr = 0;                     //    既存データはすべて廃棄
        }else{                              // 正常受信なら
            RxData = RC1REG;
            switch(RxData){                 // CRLFを受信したら、
            case 0x0A:                      //   それまでの受信データを
            case 0x0D:                      //   RxRbufに転送する
                if(RxWptr){                 //   ただし、CRLFは転送しない
                    memcpy(RxRbuf, RxWbuf, RxWptr);
                    RxRptr = RxWptr;        //   バッファ転送し受信数保存
                    RxWptr = 0;             //   ポインタリセット
                }
                break;
            case '!':                       // トリガ中止コマンド
                RESET();                    // デバイスのリセット命令
                break;
            default:                        // 一般文字なら
                RxWbuf[RxWptr++] = RxData;  //  受信バッファに保存
                if(RxWptr >= 20)RxWptr = 0; //  容量超えはすべて廃棄
                break;
            }
        }
    }
}