/******************************************************************** * PIC16F_ECG_Scope_V1a.c * * Condition: * 8MHz 内部クロック→ PLL無し * Fcy=8MH, Tcy=125ns * * CPU: PIC16F1938 * * Graphic LCD(Monochrome): SG12864A * * 初期(V1相当)作成年月日: 2017/5/16 N.Ishii * * 170517: * _V1aバージョン・アップ * 1S/Dを追加。500mS/Dとの切替は、P_ON時の、READY SWの状態で行う。 * 押さないで、P_ON:500mS/D・押して、P_ON:1S/D とする。 *********************************************************************/ #include #include "glcd_PIC16F_lib.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 nFIFO 32 /// 移動平均フィルタ関係 char cnt, ptr; int ave, Din, sum, FIFO[nFIFO]; unsigned int WAVbuffer[192]; int POT; int Index; char EndFlag; char HOLD_ON_flag = 0; char hold_status = 0; char READY_flag = 0; char ready_status = 0; char TimeDivSw; char sample_cnt; /// Function Prottypes void Oscillo(void); void AxisDraw(void); void Single_trigger_mode(void); void Oscillo_single_trigger(void); void dotyline(unsigned int x0); void dotxline(unsigned int y0); /******************************************************************************* * 割込み関数(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 (TimeDivSw) sample_cnt= 16; // 500mS/D時:0.625*16=10mS周期:波形BUFへの格納処理 else sample_cnt= 32; // 1S/D時:0.625*32=20mS周期:波形BUFへの格納処理 if (cnt == sample_cnt) { cnt= 0; WAVbuffer[Index++] = ave; if (Index >= 192) { // 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= 0b01110010; // 内部クロックは8MHzとする 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= 0b00111000; // RB3-5は、内部プルアップに指定 OPTION_REGbits.nWPUEN = 0; //プルアップ有効化 /// P_ON時の、READY SWの状態で、時間軸切替フラグを設定 if (READY_SW) TimeDivSw= 1; // 500mS/D else TimeDivSw= 0; // 1S/D /// Initialize GLCD Control Signal Level LCD_E= 0; LCD_CS1= 1; LCD_CS2= 1; LCD_RW= 1; LCD_DI= 1; lcd_Init(); // GLCD初期化 lcd_Clear(0); lcd_Str(0, 0, "ECG Scope V1a"); __delay_ms(1000); /// 移動平均フィルタ初期化 cnt= ptr= sum= 0; for(i= 0; i < nFIFO; i++) FIFO[i]= 0; POT= 820; // DC4V相当を、トリガレベルとする /// 状態変化割込み(IOC)設定 IOCIE= 0; // ここでは未だ、IOC無効 IOCBN= 0b00101000; // RB3:HOLD SW/RB5:READY SW 立下りエッジ検知 /// ADC初期設定 ADCON0= 0; // ADC無効化 ADCON1= 0x80; // 変換結果形式:右詰め・変換用CLK選択:Fosc/2・Vref= Vcc /// T2設定〜 割込み開始 /// (8M/4)/1/(124+1)/10= 1.6k(0.625mS周期:50Hzの1周期を、32サンプル)これが、ADサンプル周波数になる。 T2CON = 0b01001000; // TMR2プリスケーラ値を1:1、ポストスケーラ値は1:10の設定、ここでは未だ割込み無効(T2 OFF) PR2 = 124 ; // タイマーのカウント値を設定 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 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; /// Cheack HOLD_RUN_SW Status while (HOLD_ON_flag){ hold_status = 1; } hold_status = 0; __delay_ms(300); } else Single_trigger_mode(); } } /****************************************** * Drawing Coordinate Axis *******************************************/ void AxisDraw(void){ lcd_Clear(0); lcd_Line(0, 0, 0, 63); // Y axis_1: 左端の、Yライン描画 dotyline(51); // Y axis_2: 中央付近最初の、時間軸Yライン描画 dotyline(101); // Y axis_3: 右端付近2番目の、時間軸Yライン描画 lcd_Line(0, 32, 127, 32); // X axis dotxline(49); dotxline(17); if (TimeDivSw) lcd_Str(7,11,"500mS/D"); else lcd_Str(7,14,"1S/D"); } /******************************************************* * Disp. Oscillo (Disp. Save Buffer Data) * Detect Trigger-> Disp. 128Data from this Position * Not Detect Trigger-> Disp. 128Data from Top Position *******************************************************/ void Oscillo(void){ unsigned int i,x; AxisDraw(); // Drawing Coordinate Axis x = 0; // First Data /// Check Trigger(今回は、立上りトリガのみ) while((x < 128) && !((WAVbuffer[x]=POT))){ x++; } /// Detect Trigger-> Disp. 128Data from this Position if(x<64){ for(i=0; i<127; i++){ lcd_Line(i, WAVbuffer[i+x]/16, i+1, WAVbuffer[i+x+1]/16); } } /// Not Detect Trigger-> Disp. 128Data from Top Position else { for(i=0; i<127; i++){ lcd_Line(i, WAVbuffer[i]/16, i+1, WAVbuffer[i+1]/16); } } } /****************************************************** * Disp. Oscillo and Cheack trigger for Single mode *******************************************************/ void Oscillo_single_trigger(void){ unsigned int i,x; x = 0; // First Data /// Check Trigger(今回は、立上りトリガのみ) while((x < 128) && !((WAVbuffer[x]=POT))){ x++; } /// Detect Trigger-> Disp. 128Data from this Position if(x<64){ LED = 0; // Ready LED OFF AxisDraw(); for(i=0; i<127; i++){ lcd_Line(i, WAVbuffer[i+x]/16, i+1, WAVbuffer[i+x+1]/16); } READY_flag = 0; } } /********************************************* * Single_trigger_mode **********************************************/ void Single_trigger_mode(void) { TMR2IE= 0; IOCIE= 1; TMR2ON= 1; // T2 ON while ((!READY_flag) && (!TRIG_MODE_SW)) { ready_status = 1; } 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(); } } /******************************************* * Assist Line X Coordinate Axis (Dot Line) ********************************************/ void dotxline(unsigned int y0){ unsigned int i; for(i=0; i<128; i+=3) lcd_Pixel(i, y0-1, 1); } /******************************************* * Assist Line Y Coordinate Axis (Dot Line) ********************************************/ void dotyline(unsigned int x0){ unsigned int i; for(i=0; i<64; i+=3) lcd_Pixel(x0-1, i, 1); }