/* ***************************************************
 * ロジックアナライザー データ収集・表示
 * ***************************************************
 * コマンド受信は割込みで処理
 * クロックは4MHzXtalをPLLで20倍にし、80MHz(40MIPS)
 *
 * Target PIC dsPic33FJ128GP802
 * MPLAB X , Microchip XC16
 * Created on 2014/12/22, 17:45
 * 20211116 CXL を RESET命令に変更
 * ******************************************************/
#define FCY 40000000UL   // 80MHz / 2
#define ch1_pos     6    // Ch1のPortB入力端子位置

//*******************************************************
#include <xc.h>
#include <string.h>
#include <libpic30.h>       // 遅延ライブラリ

// コンフィギュレーション ****** XT(3-10MHz)発振 + PLL ******
_FGS(GWRP_OFF & GCP_OFF);
_FOSCSEL(FNOSC_PRIPLL);
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT);
_FWDT(FWDTEN_OFF);

// プロトタイプ *********************************************
void ckCommand(void);
void Aquition(void);
void intl_UART1(void);
void UART_str(char *, char);
void UART_rom(const char *);
void outAqData(int *, int);

// 共通変数 *************************************************
int __attribute__((address(0x800))) AqData[1200];             // 収集データバッファ
int Err_Cmd;                 // エラー発生フラグ
int freeRun;                 // フリーランコマンド受信
int MskPtn;                   // トリガマスクパターン
int PrePtn;                   // トリガ準備パターン
int TrgPtn;                   // トリガパターン
int AqSat;                    // 収集開始
int AqEnd;                    // 収集終了
int PortData;                 // 入力ポートデータ
int ChckData;                 // トリガ検査データ
int AqPtr;                    // 収集ポインタ
int RcdNo;                    // 記録データ数
unsigned char RxWptr,RxRptr;  // UART Rx文字数
char RxWbuf[20],RxRbuf[20];   // UART Rxバッファ

// メイン ***********************************************************
int main (void){
    // -------------- PLLに関連する設定 ----------------------------
    PLLFBD = 78;                 // M = 80  4MHz x80/2/2
    CLKDIVbits.PLLPOST=0;        // N2 = 2          = 80MHz
    CLKDIVbits.PLLPRE=0;         // N1 = 2
    while (OSCCONbits.LOCK!=1);  // PLLの安定(LOCK)を待つ
    // ---------------- 初期設定 ------------------------------------
    Err_Cmd = 0;                 // エラー発生フラグ

    // -----------------------------------
    T1CON = 0x0010;              // Timer1 PS 1:8 初期設定
    RxWptr = 0;                  // 受信書込みポインタのリセット
    RxRptr = 0;                  // 受信読込みポインタのリセット
    intl_UART1();                // UART設定
    _U1RXIP = 4;                 // Rx割込優先度を4に設定 (既定値)
    _U1RXIF = 0;                 // Rx割込フラッグをクリア
    _U1RXIE = 1;                 // Rx割込を許可
    // ---------------- メインループ -------------------------------
    while(1){                    // ----------コマンド受信----------
        while(!RxRptr);          // 待ち
        ckCommand();             // コマンドの処理
        RxRptr = 0;              //   受信処理終了を明示
        if(Err_Cmd){             //   処理エラーがあった場合
            UART_rom("!\r\n");   //     再送要求を送信
            continue;            //     受信待ちに戻る
        }else{                   //   正常終了
            UART_rom("#\r\n");   //     正常終了を送信
        }
                                 // -----------データの収集-----------
        Aquition();
                                 // -----------データ送信-------------
        outAqData(AqData+AqPtr, 1100-AqPtr);   // 収集
        outAqData(AqData, AqPtr);              // 収集データ送信
        UART_rom("##\r\n");                    // データ終了を送信
    }
    return 0;
}

/* ********************************************************************
 * PCからのコマンドでトリガ条件を設定する
 * #〇〇〇#〇〇
 *  ||| |└Trigger位置
 *  ||| └ Sample Rate
 *  ||└ Ch3 Trigger条件
 *  |└─ Ch2 Trigger条件
 *  └── Ch1 Trigger条件
 * Err_Cmd 「1」での復帰はエラーを示す
 * 使用する共通変数
 *   Err_Cmd, RxRptr, RxRbuf
 *   MskPtn, PrePtn, TrgPtn
 *   PR1
 *   AqSat, AqEnd
 * *******************************************************************/
void ckCommand(void){
    int i;
    Err_Cmd = 1;                     // 受信文字列のエラー確認
    if(RxRptr != 7)return;           // 受信文字数の確認
    if(RxRbuf[0] != '#')return;      // コマンド分離の確認
    if(RxRbuf[4] != '#')return;      // コマンド分離の確認
    Err_Cmd = 0;

    // ----------- 各ChのTrigger条件設定 -------------------------
    MskPtn=0;PrePtn=0;TrgPtn=0;      // トリガパターン クリア
    for(i=3; i>0; i--){              // Ch3 -> Ch1を処理
        MskPtn<<=1; PrePtn<<=1; TrgPtn<<=1;
        switch(RxRbuf[i]){
            case '0':break;                            // * 任意
            case '1':MskPtn++;PrePtn++;TrgPtn++;break; // 1 Hi
            case '2':MskPtn++;                  break; // 0 Low
            case '3':MskPtn++;         TrgPtn++;break; // / 立上り
            case '4':MskPtn++;PrePtn++;         break; // \ 立下り
            default:Err_Cmd = 1;return;                // 指定文字以外
        }
    }
    // Ch位置をボードの接続端子位置に合わせる
    MskPtn<<=ch1_pos; PrePtn<<=ch1_pos; TrgPtn<<=ch1_pos;

    // ------------------ Sample Rateの設定 ---------------------
    switch(RxRbuf[5]){
        case '0':PR1 =     4;break;       //   1us
        case '1':PR1 =     9;break;       //   2us
        case '2':PR1 =    24;break;       //   5us
        case '3':PR1 =    49;break;       //  10us
        case '4':PR1 =    99;break;       //  20us
        case '5':PR1 =   249;break;       //  50us
        case '6':PR1 =   499;break;       // 100us
        case '7':PR1 =   999;break;       // 200us
        case '8':PR1 =  2499;break;       // 500us
        case '9':PR1 =  4999;break;       //   1ms
        case 'A':PR1 =  9999;break;       //   2ms
        case 'B':PR1 = 24999;break;       //   5ms
        case 'C':PR1 = 49999;break;       //  10ms
        default:Err_Cmd = 1;return;       // エラー 指定文字以外
    }

    // ------------------- 収集開始および終了の指定 --------------------
    freeRun = 0;
    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;freeRun= 1;break;    // フリーラン
        case '4':AqSat= 550;AqEnd= 550;break;    // トリガは中央
        case '5':AqSat=1000;AqEnd= 100;break;    // トリガは後方
        default:Err_Cmd = 1;return;              // エラー 指定文字以外
    }
}

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

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

    do{ // ---------------- トリガ条件の確認 -------------------------
        do{ // -------------- 前条件の確認 ---------------------------
            while(!_T1IF);                 // 取得タイミングを待つ
            _T1IF = 0;                     //
            PortData = PORTB;              // データ読込
            AqData[AqPtr++] = PortData;    // 保存
            if(AqPtr > 1100) AqPtr = 0;    // 保存ポインタを循環
            ChckData = PortData & MskPtn;  // 条件検査用データ作成
        }while(ChckData != PrePtn);        // トリガ前条件成立まで待つ

        do{ // ----------------トリガ条件の成立待機 ------------------
            while(!_T1IF);                  // 取得タイミングを待つ
            _T1IF = 0;                      //
            PortData = PORTB;               // データ読込
            AqData[AqPtr++] = PortData;     // 保存
            if(AqPtr > 1100) AqPtr = 0;     // 保存ポインタを循環
            ChckData = PortData & MskPtn;   // 条件検査用データ作成
        }while(ChckData ==PrePtn);          // トリガ前条件成立なら繰返
    }while(ChckData !=TrgPtn);              // トリガ条件一致で抜ける

    do{ // ------------------- トリガ後保存 ----------------------------
        while(!_T1IF);                      // 取得タイミングを待つ
        _T1IF = 0;                          //
        PortData = PORTB;                   // データ読込
        AqData[AqPtr++] = PortData;         // 保存
        if(AqPtr > 1100) AqPtr = 0;         // 保存ポインタを循環
    }while(++RcdNo  < AqEnd);               // AqSat(取得完了)数まで保存

    T1CONbits.TON = 0;                      // Timer1停止
}

/* ********************************************************************
 * UARTの初期設定
 * 115200bps @ 40MHz
 * *******************************************************************/
void intl_UART1(void){
//    U1BRG  = 259;                   // 9600bps @ 40MHz
    U1BRG  = 21;                      // 115200bps @ 40MHz
    U1MODE = 0x8000;                  // 8bit, No Parity,
    U1STA  = 0x0400;                  // No Flow Control
    _PCFG4 = 1;                       // UART Portをデジタル設定
    _PCFG5 = 1;                       // UART Portをデジタル設定
    _U1RXR = 2;                       // UART1 RX を RP2(RB2 AN4)に設定
    _RP3R  = 3;                       // UART1 TX を RP3(RB3 AN5)に設定
    __delay_ms(200);                  // UART安定動作を待つ遅延
}
/* ********************************************************************
 * UART1 文字列送信
 * 文字配列の指定文字数だけ 送信する
 * *******************************************************************/
void UART_str(char *str, char ptr){
    while(ptr--){                     // 指示文字数分繰り返す
        while(U1STAbits.UTXBF);       // 送信バッファが空なら
        U1TXREG = *str++;             // 一文字送信 ポインタを進める
    }
}
/* ********************************************************************
 * UART1 ROM文字列送信
 * 指定文字列を送信する
 * *******************************************************************/
void UART_rom(const char *str){
    while(*str){                      // 終端の「0」になるまで繰り返す
        while(U1STAbits.UTXBF);       // 送信バッファが空なら
        U1TXREG = *str++;             // 一文字送信 ポインタを進める
    }
}
/* ********************************************************************
 * Data配列送信
 * 配列のデータを送信する  一行50文字+CRLF
 * adata:Data配列名  ptr:データ数
 * ch1_pos;データのあるbit位置
 * *******************************************************************/
void outAqData(int *adata, int ptr){
    int i = 0;
    char c;
    while(ptr--){                  // 指示文字数分繰り返す
        c = *adata++ >> ch1_pos;       // データを取り出しポインタを進める
        c = c & 0xF;                   // 下位4ビットを取り出す
        c = c | '@';                   // 「@」からの文字コードに変換
        while(U1STAbits.UTXBF);        // 送信バッファが空なら
        U1TXREG = c;                   // 一文字送信
        if((++i >= 50) | (ptr == 0)){  // 50文字毎または
            i = 0;                     // 指定数送信完了で
            UART_rom("\r\n");          // CRLFを挿入
        }
    }
}

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