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