PIC16F18346の基本動作から応用プログラムまでを学びます。

ホーム
12F1822
16F1455
16F1459
18F14K50
18F26J50
PIC
その他
    
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
ロジックアナライザー
2021-09-23

PIC16F18346を使用し、ロジックアナライザーを作成しました。PICを最高速の 32MHzで動作させTimer0で定期的にロジック信号を取り込み内部RAMに収集していきます。電子工作にあったら便利なロジックアナライザーを目標としました。

全てC言語 (XC8)でコーディングしたため、サンプルレートの最高は 10μSecです。1回に1100サンプル読み込みます。また、現在使用している入力端子は、RC0-RC3の4チャンネルです。さらに、RC4-RC7を追加することは簡単な変更で可能ですが、PC側の操作パネルの見栄えの点から、現在必要としている4チャンネルだけにしています。 収集したデータはUSBでPCに送り表示させます。PICからはシリアル115200bpsで出力し、「シリアル - USB変換」でPCのCOMポートに接続しています。

このPICロジックアナライザーを操作するPCアプリケーションは 次ページで紹介します。

プログラム

main.c プログラムを示します。

main.c

/*************************************
 * File:18346_LgiTool_3c.X    = c c c c =
 * 4Ch ロジックツール Sampl rateはmax 10 us」
 * System ClockはConfigで内部32MHzに設定
 * PIC16F18346
 * Created on 2021-09-22
 **************************************/

 #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 (32MHz)
#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 CXL = false;             // キャンセル受信したフラグ
bool Err_Cmd = false;         // コマンドがエラー時のフラグ
bool freeRun = 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;                  // トリガパターン
int AqSat;                    // 収集開始
int AqEnd;                    // 収集終了
int AqPtr;                    // 収集ポインタ
int RcdNo;                    // 記録データ数

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

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

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

    TRISA  = 0b111111;          // Port すべて入力
    TRISB  = 0b11111111;        // Port すべて入力
    TRISC  = 0b11111111;        // Port すべて入力
    ANSELA = 0;                 // すべてデジタル
    ANSELB = 0;                 // すべてデジタル
    ANSELC = 0;                 // すべてデジタル
    WPUA   = 0b001000;          // RA5 弱プルアップ ON
    WPUC   = 0b00010000;        // RA5 弱プルアップ ON
    RXPPS = 0x0D;   //RB5->EUSART:RX;
    RB7PPS = 0x14;   //RB7->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;

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

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

/* ********************************************************************
 * コマンドの解釈・処理
 * *******************************************************************/
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条件設定 -------------------------
    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 = true;return;             // 指定文字以外
        }
    }
    // ------------------ 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;       // エラー 指定文字以外
    }

    // ------------------- 収集開始および終了の指定 --------------------
    freeRun = false;
    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=true;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;                             // 収集ポインタ リセット
    int cntPre  = AqSat;                   // トリガ前 収集データ数
    int cntPost = AqEnd;                   // トリガ後 収集データ数

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

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

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

    // ------------------- トリガ後保存 ----------------------------
    while(cntPost--){                   // AqEnd(取得完了)数まで保存
        while(!TMR0IF);                 // 取得タイミングを待つ
        TMR0IF = 0;                     //
        PortData = PORTC;               // データを読込
        AqData[AqPtr++] = PortData;     // 保存
        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 & 0xF;                   // 下位4ビットを取り出す
        c = c | '@';                   // 「@」からの文字コードに変換
        putch(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 '!':                       // トリガ中止コマンド
                CXL = 1;                    // 中止フラッグをON
                break;
            default:                        // 一般文字なら
                RxWbuf[RxWptr++] = RxData;  //  受信バッファに保存
                if(RxWptr >= 20)RxWptr = 0; //  容量超えはすべて廃棄
                break;
            }
        }
    }
}