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