/******************************************************************** * PIC16F_ECG_Scope_V3.c * * 16MHz 内部クロック→ PLL無し * Fcy=16MHz, Tcy=62.5ns * * CPU: PIC16F1938 * * 2.4インチQVGA液晶モジュール(aitendo): UL024TF * * デバッグ年月日: 2017/6/26 N.Ishii *********************************************************************/ #include #include "colorlcd_libPIC16FVH.h" // コンフィギュレーション1の設定 #pragma config FOSC = INTOSC // 内部クロックを使用する(INTIO) #pragma config WDTE = OFF // ウオッチドッグタイマー無し(OFF) #pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON) #pragma config MCLRE = ON // 外部リセット信号使用 #pragma config CP = OFF // プログラムメモリーを保護しない(OFF) #pragma config CPD = OFF // データメモリーを保護しない(OFF) #pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON) #pragma config CLKOUTEN = OFF // CLKOUTピンをRA6ピンで使用する(OFF) #pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF) #pragma config FCMEN = OFF // 外部クロック監視しない(OFF) // コンフィギュレーション2の設定 #pragma config WRT = OFF // Flashメモリーを保護しない(OFF) #pragma config VCAPEN = OFF // 低電圧レギュレータ用のキャパシタは使用しない(OFF) #pragma config PLLEN = OFF // 動作クロックを32MHz(4xPLL)では動作させない(OFF) #pragma config STVREN = ON // スタックがオーバフローやアンダーフローしたらリセットをする(ON) #pragma config BORV = HI // 電源電圧降下常時監視電圧(2.5V)設定(HI) #pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF) #define LED RC0 #define HOLD_SW PORTBbits.RB3 #define READY_SW PORTBbits.RB5 #define TRIG_MODE_SW PORTBbits.RB4 #define TRIG_LEVEL_SW PORTBbits.RB1 #define SAMPLE_TIME_SW PORTBbits.RB2 #define nFIFO 32 /// 移動平均フィルタ関係 char cnt, ptr; int ave, Din, sum, FIFO[nFIFO]; unsigned int WAVbuffer[416]; // トリガ検知に、95ワード(トリガ電圧約1.5V〜2.5V)+トリガ点からの波形描画に、320+1ワード割り当て int POT; int Index; char EndFlag; char HOLD_ON_flag = 0; char hold_status = 0; char READY_flag = 0; char ready_status = 0; char sample_cnt; /// Function Prottypes void Oscillo(void); void AxisDraw(void); void DispMeas(void); void Single_trigger_mode(void); void Oscillo_single_trigger(void); /******************************************************************************* * 割込み関数(xc8は、ベクタが1つしかないので、各割込みフラグを見て各処理に分岐) * (1) タイマー2割込みの処理 *    0.625mS周期:移動平均フィルタ処理+10mS周期:波形BUFへの格納処理 * (2) 状態変化割込み(IOC)処理 *    SWが押された時のリード処理 *******************************************************************************/ void interrupt ISR( void ) { if(PIR1bits.TMR2IF == 1) { TMR2IF= 0; // タイマー2割込フラグをリセット ADCON0= 0x31; // AN12選択、ADC有効化 __delay_us(20); // アクイジション待ち ADCON0bits.GO= 1; // AD変換開始 while(ADCON0bits.GO); // AD変換完了待ち Din= ADRESL+(ADRESH*256); // 10bitの値に変換して格納 /// 移動平均フィルタ処理:0.625mS周期 sum= sum - FIFO[ptr] + Din; // 変化成分だけを使って移動平均を計算する。 ave= sum/nFIFO; FIFO[ptr]= Din; // FIFOに、データを格納する。 ptr++; // FIFOポインタ更新 if (ptr == nFIFO) ptr=0; // FIFOの深さ(32ワード)を超えたら、ポインタをクリア cnt++; if (!SAMPLE_TIME_SW) sample_cnt= 8; // 250mS/D時:0.625*8=5mS周期:波形BUFへの格納処理 else sample_cnt= 16; // 500mS/D時:0.625*16=10mS周期:波形BUFへの格納処理 if (cnt == sample_cnt) { cnt= 0; WAVbuffer[Index++] = ave; if (Index >= 416) { // Buffer Full ? ADCON0= 0; // ADC無効化 TMR2IE= 0; EndFlag = 1; } } } else { if (INTCONbits.IOCIF == 1) { IOCBF3= 0; // IOC割込フラグ(RB3:HOLD SW)をリセット IOCBF5= 0; // IOC割込フラグ(RB5:READY SW)をリセット __delay_ms(20); // スイッチ安定待ち→ アクティブ時 if (!hold_status) { // hold_status -> 0: RUN if (!HOLD_SW) { HOLD_ON_flag = 1; } } else { // hold_status -> 1: HOLD_ON Loop if (!HOLD_SW) { HOLD_ON_flag = 0; } } if (ready_status){ if (!READY_SW) READY_flag = 1; } __delay_ms(10); // スイッチ安定待ち→ リリース時 } } } /******************************************************************************* * メインの処理 *******************************************************************************/ void main(void) { char i; OSCCON= 0b01111010; // 内部クロックは16MHzとする ANSELA= 0b00000000; // AN0-AN4は使用しない全てデジタルI/Oとする ANSELB= 0b00000001; // AN12(RB0)のみアナログ入力、他は全てデジタルI/Oとする LCD_TRIS= 0b00000000; // ピン(RA)は全て出力に割当てる(0:出力 1:入力) TRISB= 0b11111111; // ピン(RB)は全て入力に割当てる TRISC= 0b00000000; // ピン(RC)は全て出力に割当てる LED= 0; // LED OFF WPUB= 0b00111110; // RB1-5は、内部プルアップに指定 OPTION_REGbits.nWPUEN = 0; //プルアップ有効化 // 液晶表示器の初期化 lcd_Init(); lcd_Str(0, 0, "ECG Scope V2", CYAN, BLACK); __delay_ms(1000); /// 移動平均フィルタ初期化 cnt= ptr= sum= 0; for(i= 0; i < nFIFO; i++) FIFO[i]= 0; /// 状態変化割込み(IOC)設定 IOCIE= 0; // ここでは未だ、IOC無効 IOCBN= 0b00101000; // RB3:HOLD SW/RB5:READY SW 立下りエッジ検知 /// ADC初期設定 ADCON0= 0; // ADC無効化 ADCON1= 0x80; // 変換結果形式:右詰め・変換用CLK選択:Fosc/2・Vref= Vcc /// T2設定〜 割込み開始 /// (16M/4)/1/(249+1)/10= 1.6k(0.625mS周期:50Hzの1周期を、32サンプル)これが、ADサンプル周波数になる。 T2CON = 0b01001000; // TMR2プリスケーラ値を1:1、ポストスケーラ値は1:10の設定、ここでは未だ割込み無効(T2 OFF) PR2 = 249; // タイマーのカウント値を設定 lcd_Clear(BLACK); // 最初だけ、全画面クリアを行う TMR2 = 0; // タイマー2の初期化 TMR2IF = 0; // タイマー2割込フラグを0にする TMR2IE = 0; // ここでは未だT2割込み無効 PEIE = 1; // 周辺装置割り込み有効 GIE = 1; // 全割込み処理を許可する while(1){ /// Draw a Graph of Input Wave Data Index = 0; EndFlag = 0; // Clear End of Convertion Flag if (TRIG_MODE_SW) { // Trigger is AUTO Mode if (TRIG_LEVEL_SW) POT= 820; // 2.4V相当 else POT= 512; // 1.5V相当 LED = 0; // Ready LED OFF READY_flag = 0; ready_status = 0; IOCIE= 0; // AD変換中は、IOC(SW割込み)禁止 TMR2IE= 1; TMR2ON= 1; // T2 ON while(!EndFlag); // EOC待ち // バッファリング終了で抜ける(この時、TMR2IE=禁止にしてる) IOCIE= 1; // 変換終了後に、IOC許可→ こうしないと、バッファフルの途中でSW割込みが入り // 正しい波形をホールド出来ない場合がある。 Oscillo(); // Disp. Oscillo EndFlag = 0; Index = 0; } else Single_trigger_mode(); } } /****************************************** * Drawing Coordinate Axis *******************************************/ void AxisDraw(void){ int i; /// Singleモードの時のみ、以下を実行:シングル時に、前の波形が残る多重ホールド波形表示をさせない対策 if (!TRIG_MODE_SW) lcd_Clear(BLACK); lcd_Line(0, 0, 0, 231, BROWN); // Y axis:左端ライン for(i=50; i<319; i+=50) lcd_Line(i, 13, i, 231, BROWN); // Y axis:残6本分のライン for(i=0; i<239; i+=58) lcd_Line(0, i, 319, i, BROWN); // X axis } /******************************************* * 計測条件表示関数 ********************************************/ void DispMeas(void){ if (!SAMPLE_TIME_SW) lcd_Str(19,16,"250mS/D", CYAN, BLACK); else lcd_Str(19,16,"500mS/D", CYAN, BLACK); } /******************************************************* * Disp. Oscillo (Disp. Save Buffer Data) * Detect Trigger-> Disp. 320Data from this Position * Not Detect Trigger-> Disp. 320Data from Top Position *******************************************************/ void Oscillo(void){ // unsigned int i,x, Point; int i,x, Point; AxisDraw(); // 座標表示(この中では、全画面クリアはしない) DispMeas(); // 計測条件読込み表示処理 Point = -1; // 最初のデータ /// トリガチェック(今回は、立上りトリガのみ) for(x=0; x<95; x++){ if((WAVbuffer[x] < POT) && (WAVbuffer[x+5] >= POT)){ Point = x; continue; } } /// トリガ成立、その位置から波形表示 if(Point != -1){ for(i=1; i<=320; i++) { lcd_Line(i, WAVbuffer[i+Point-1]*5/22, i, WAVbuffer[i+Point]*5/22, MAGENTA); } /// 指定色で、サンプルデータを0.05秒間表示後、波形のみをクリア(黒で描画) __delay_ms(50); // 波形表示中に、Hold SWが押されたら、画面をホールド→ もう一度、Hold SWを押すと解除 while (HOLD_ON_flag) { hold_status = 1; } hold_status = 0; // 波形のみをクリア(黒で描画) for(i=1; i<=320; i++) { lcd_Line(i, WAVbuffer[i+Point-1]*5/22, i, WAVbuffer[i+Point]*5/22, BLACK); } // 波形のみをクリアすると、波形線と座標線が交差した座標線の一部が途切れるので、再度座標描画する。 AxisDraw(); } // トリガ不成立、最初から表示する else { for(i=1; i<=320; i++){ lcd_Line(i, WAVbuffer[i-1]*5/22, i, WAVbuffer[i]*5/22, MAGENTA); } /// 指定色で、サンプルデータを0.05秒間表示後、波形のみをクリア(黒で描画) __delay_ms(50); while (HOLD_ON_flag) { hold_status = 1; } hold_status = 0; for(i=1; i<=320; i++){ lcd_Line(i, WAVbuffer[i-1]*5/22, i, WAVbuffer[i]*5/22, BLACK); } AxisDraw(); } } /****************************************************** * Disp. Oscillo and Cheack trigger for Single mode *******************************************************/ void Oscillo_single_trigger(void){ int i,x, Point; Point = -1; // 最初のデータ /// トリガチェック(今回は、立上りトリガのみ) for(x=0; x<95; x++){ if((WAVbuffer[x] < POT) && (WAVbuffer[x+5] >= POT)){ Point = x; continue; } } /// トリガ成立、その位置から波形表示 if(Point != -1){ LED = 0; // Ready LED OFF AxisDraw(); for(i=1; i<=320; i++) { lcd_Line(i, WAVbuffer[i+Point-1]*5/22, i, WAVbuffer[i+Point]*5/22, MAGENTA); } READY_flag = 0; } /// トリガ不成立、Retry sample } /********************************************* * Single_trigger_mode **********************************************/ void Single_trigger_mode(void) { TMR2IE= 0; IOCIE= 1; TMR2ON= 1; // T2 ON DispMeas(); // 計測条件読込み表示処理 /// READY SW ON 待ちループ while ((!READY_flag) && (!TRIG_MODE_SW)) { ready_status = 1; } __delay_ms(50); // SINGLEから、AUTOトリガに切替えた瞬間のチャッタ防止のための待ち(チャッタで誤判断し // 1度、信号入力状態(READY LED点灯→ADC開始)になるのを防ぐための待ち) // BNC入力に信号が有る状態で、誤動作すると、AUTOに戻った時に、ホールドされた波形が変に残ることがあった。 if (!TRIG_MODE_SW) { ready_status = 0; LED = 1; // Ready LED ON ADCON0= 0; // ADC無効化 TMR2IE= 1; TMR2ON= 1; // T2 ON while(!EndFlag); Oscillo_single_trigger(); } else lcd_Clear(BLACK); }