/************************************************************************* * easy_fra_v3 プログラム * * <ハード概要> * 周波数スイープとして、自作の「PIC18F_DDS_AF_OSC_V2a」を使用する。 * ログアンプを使用。縦軸は、dB軸とする。 * * 2.4インチ・カラーQVGA液晶モジュール(aitendo): UL024TF * * <v2cからの変更点> * @ タクトSWを、1個追加する。→ RB1ポート使用 *  DDS_OSCを、NORMALモードで固定F出力にして。レベル合わせをする *  (これを、CALモードと呼ぶことにする。)時の、スタートSWとして使う。 *  また、このモードの時は、周波数表示と、181ステップ終了時の計測表示は *  無しとし、dB値の表示のみとする。 * * A v2b(カーソル計測モード追加版)からの不具合点を改善した。 *  今迄妥協して使用していたが、以下を改善した。 *  最初の同期正パルス:1mS幅の、立下りエッジ検知後、1画面描画 *  終了すると、カーソル計測モードになるが、そこでは、長押しと単発検知を兼用 *  するために、長めの遅延:100mSを、SW読込み前に挿入している。そしてその後 *  エッジ検知(同期正パルス:1mS幅)を行っているため、2回目以降のエッジ検知 *  が出来なくなる。 *  操作上ここで、RESET SWを押せば再び、最初の同期正パルス待ちになり *  再スタート出来るので、これで良しとしていた。 * *  これを改善する方法として、メインループでのエッジセンスをやめ、 *  CN割込みによる検知に変更した。 * *  途中での中断の時のみ、RESET SWを使うことにした。 * * Condition: * 8MHz External X'tal Oscillator, 10x PLL (8MHzx10= 80MHz) * Fcy=80MHz/2=40MHz, Tcy=25ns * * MPU: dsPIC33FJ64GP802 * * N.Ishii 2017.8.14 **************************************************************************/ #include "p33FJ64GP802.h" #include "colorlcd_libdsPICVH.h" #include /// コンフィギュレーション設定 80MHz _FOSCSEL(FNOSC_PRIPLL & IESO_OFF); // Prim OSC _FOSC(FCKSM_CSDCMD & IOL1WAY_OFF & OSCIOFNC_OFF & POSCMD_XT); _FWDT(FWDTEN_OFF); _FPOR(ALTI2C_OFF & FPWRT_PWR32); _FICD(JTAGEN_OFF & ICS_PGD1); #define MoveRightCur_SW PORTBbits.RB2 #define MoveLeftCur_SW PORTBbits.RB3 #define CAL_START_SW PORTBbits.RB1 #define SYNC_PULSE PORTAbits.RA1 /// メッセージ・テーブル char MsgData1[] = "xxxxxxHz"; char MsgData2[] = "xx.xxdB"; /// A Variable (Global) char cal_start_flag= 0; char NegEdgeSence= 0; char EndFlag= 0; int x,y; // 周波数軸の座標 int Xcur= 0; // カーソル座標 int Ycur= 224; int X_log; int X_log_temp; int y_temp; unsigned long fx; float db; /// 計測値バッファ float Buffer_db[181]; // 各周波数の、dB値が格納されている。 char Buffer_pol[181]; // dB値の極性が格納されている。0:なし、1:+、-1:− /// Function Prottypes void AxisDraw(void); void ltostring(char digit, unsigned long data, char *buffer); void ftostring(long seisu, long shousu, float data, char *buffer); /***************************************************** * 状態変化割り込み処理:1mS幅同期パルス読込み処理 ******************************************************/ void __attribute__((interrupt, no_auto_psv)) _CNInterrupt(void) { IFS1bits.CNIF = 0; // 割り込みフラグクリア delay_ms(2); // 1mS幅正パルスの、立下りを読むので、2mS遅延 if(!SYNC_PULSE) NegEdgeSence= 1; } /************************************************ * Function Main ************************************************/ int main(void){ /// Initialize Clock CLKDIVbits.PLLPRE = 0; // PLLPRE=1/2 to 4MHz PLLFBDbits.PLLDIV = 40; // PLLDIV=1/40 to 160MHz CLKDIVbits.PLLPOST = 0; // PLLPOST=1/2 to 80MHz then 40MIPS /// Initialize Clock OSCCON = 0x00300; // PRIPLL指定 CLKDIV = 0x0100; // クロックの分周1:2に設定 PLLFBD = 0x0026; // 40倍 8MHz÷2×40÷2 = 80MHz /// Initialize Port AD1PCFGL = 0xFFFA; // AN0,2 are Analog(AN1は、RA1のデジタル入力(スイープ同期信号入力)として使用) TRISA = 0x000F; // RA4 is Out, Other is Input TRISB = 0x000F; // Set QVGA Port Direction CNPU1 = 0x00E0; // RB1,2,3 pull up /// 状態変化割込み設定 CNEN1= 0x0008; // CN3(RA1:1mS幅同期パルス) 状態変化割り込み有効 /// Initialize GLCD lcd_Init(); lcd_Clear(BLACK); // クリア lcd_Str(0, 0, "Start Test", CYAN, BLACK); delay_ms(1000); /// Initialize ADC AD1CON1= 0x0000; // SAMP bit= 0 ends sampling... and starts converting(手動AD変換) AD1CHS0= 0x0000; // Connect AN0 as CH0 input AD1CSSL= 0x0000; // 自動スキャンしない AD1CON3= 0x0002; // 手動サンプル、Tad= internal 2Tcy AD1CON2= 0x0000; // Vref=AVdd-AVss,入力スキャンしない、CH0を変換 AD1CON1bits.ADON = 1; // ADC Start /// Main Loop while(1){ IEC1bits.CNIE= 1; // 状態変化割り込み許可 fx= 8; // 表示周波数初期化(10-2=8Hz) if((EndFlag) && (!cal_start_flag)) { // スイープ・モードかつ、画面の描画終了だったら以下を実行 lcd_Line(Xcur, Ycur, Xcur, 219, CYAN); // イニシャル位置に縦カーソルを描画 lcd_Str(22, 13, "10Hz", CYAN, BLACK); // 10Hz表示 fx= 10; // カーソルモード時の周波数表示初期値(簡易的にresetで再開なので、通常時と同じ変数でOK) /// dB値を求め、液晶に表示する。 if(Buffer_pol[Xcur] == 0) lcd_Char(18, 14, 0x20, CYAN, BLACK); // 極性部をブランク表示にする。 if(Buffer_pol[Xcur] == 1) lcd_Char(18, 14, 0x2B, CYAN, BLACK); // '+'表示 if(Buffer_pol[Xcur] == -1) lcd_Char(18, 14, 0x2D, CYAN, BLACK); // '-'表示 ftostring(2, 2, Buffer_db[Xcur], MsgData2); // 浮動小数点→ 文字列変換 lcd_Str(19, 14, MsgData2, CYAN, BLACK); // dB表示 } do { /// カーソル移動による計測モードの開始 if((EndFlag) && (!cal_start_flag)) { // スイープ・モードかつ、画面の描画終了だったら以下を実行 delay_ms(100); // スイッチ安定待ち ※ レベルとエッジ検知を共用する為、長めに設定 if((!MoveRightCur_SW) || (!MoveLeftCur_SW)) { /// 移動する前に現在位置のカーソルを背景色と同じ黒で塗りつぶす(消すと同じ) X_log= (int)((log10(fx)-1)*45);// Xcur座標をログ座標に変換 lcd_Line(X_log, Ycur, X_log, 219, BLACK); /// カーソル移動処理 if (!MoveRightCur_SW) { Xcur++; /// 周波数値を求める。 if(Xcur <= 45) fx += 2; if((Xcur >= 46) && (Xcur <= 90)) fx += 20; if((Xcur >= 91) && (Xcur <= 135)) fx += 200; if((Xcur >= 136) && (Xcur <= 180)) fx += 2000; if (Xcur == 181) { Xcur= 180; fx= 100000; } } if (!MoveLeftCur_SW) { Xcur--; /// 周波数値を求める。 if(Xcur <= 44) fx -= 2; if((Xcur >= 45) && (Xcur <= 89)) fx -= 20; if((Xcur >= 90) && (Xcur <= 134)) fx -= 200; if((Xcur >= 135) && (Xcur <= 179)) fx -= 2000; if (Xcur == -1) { Xcur= 0; fx= 10; } } /// ログ移動した位置にカーソルを描画する X_log= (int)((log10(fx)-1)*45);// Xcur座標をログ座標に変換 lcd_Line(X_log, Ycur, X_log, 219, CYAN); /// dB値を求め、液晶に表示する。 if(Buffer_pol[Xcur] == 0) lcd_Char(18, 14, 0x20, CYAN, BLACK); // 極性部をブランク表示にする。 if(Buffer_pol[Xcur] == 1) lcd_Char(18, 14, 0x2B, CYAN, BLACK); // '+'表示 if(Buffer_pol[Xcur] == -1) lcd_Char(18, 14, 0x2D, CYAN, BLACK); // '-'表示 ftostring(2, 2, Buffer_db[Xcur], MsgData2); // 浮動小数点→ 文字列変換 lcd_Str(19, 14, MsgData2, CYAN, BLACK); // dB表示 /// 周波数値を液晶に表示する。 ltostring(6, fx, MsgData1); // バイナリ→ 文字列変換 lcd_Str(18, 13, MsgData1, CYAN, BLACK); // 周波数表示 } } if(!CAL_START_SW) { cal_start_flag= 1; fx= 8; } } while((!NegEdgeSence) && (!cal_start_flag)); // スイープ同期パルスの立下りエッジが検知されるか、CALスタートSWが // 押されるまでエッジ検知及び、SWのレベルセンスを繰り返す。 // (立下りエッジから、スイープを開始する。) if(NegEdgeSence) { // 立下りエッジ検知で抜けた場合 cal_start_flag= 0; // CALフラグをリセット NegEdgeSence= 0; // NegEdgeSenceフラグをリセット IEC1bits.CNIE= 0; // 状態変化割り込み禁止 fx= 8; Xcur= 0; } AxisDraw(); // 座標表示 if(cal_start_flag) lcd_Str(0, 0, "CAL START", WHITE, BLACK); /// 181回、x座標をずらしながら、サンプル→ 変換→ ドット描画を繰返し、 /// 1画面分のグラフを描画 for(x= 0; x < 181; ++x) { // delay_ms(500); // 実測、506mS delay_ms(494); // 実測、500mS AD1CON1bits.SAMP= 1; // サンプリング開始 // delay_ms(100); // 実測、101.2mS delay_ms(99); // 実測、100.08mS AD1CON1bits.SAMP= 0; // AD変換開始 while(!AD1CON1bits.DONE); // AD変換終了待ち /// 変換値をドット描画の、Y座標(レベル)に変換、そこにドット描画する。 y= (ADC1BUF0)*5/22; // ADC1BUF0)*5/22は、数学的なy座標になる→ 下が0で上に行くほど増える) /// スイープ中の、周波数値を求める。 if(x <= 45) fx += 2; if((x >= 46) && (x <= 90)) fx += 20; if((x >= 91) && (x <= 135)) fx += 200; if((x >= 136) && (x <= 180)) fx += 2000; X_log= (int)((log10(fx)-1)*45);// x座標をログ座標に変換 if(x == 0) lcd_Pixel(x,239-y,WHITE); // 最初は無条件でドット描画 else { // 2回目からは、ライン描画 lcd_Line(X_log_temp, y_temp, X_log, y, WHITE); } /// 現在の座標データを、テンポラリに退避(次回描画時の始点座標になる) X_log_temp= X_log; y_temp= y; /// スイープ・モードの時のみ、スイープ中の、周波数値を表示する。 if(!cal_start_flag) { ltostring(6, fx, MsgData1); // バイナリ→ 文字列変換 lcd_Str(18, 2, MsgData1, WHITE, BLACK); // 周波数表示 } /// dB値を求め、液晶に表示する。 if(y == 177) { // 0db db= 0; lcd_Char(18, 3, 0x20, WHITE, BLACK); // 極性部をブランク表示にする。 Buffer_pol[x]= 0; // 計測バッファに極性を格納 } if(y < 177) { // −dB領域 db= (177-y)*0.25; lcd_Char(18, 3, 0x2D, WHITE, BLACK); // '-'表示 Buffer_pol[x]= -1; } if(y > 177) { // +dB領域 db= (y-177)*0.25; lcd_Char(18, 3, 0x2B, WHITE, BLACK); // '+'表示 Buffer_pol[x]= 1; } ftostring(2, 2, db, MsgData2); // 浮動小数点→ 文字列変換 lcd_Str(19, 3, MsgData2, WHITE, BLACK); // dB表示 Buffer_db[x]= db; // 計測バッファに、dB値を格納 /// スイープ・モードの時のみ、-3dBポイントを検出したら、その時の周波数を別ロケーションに表示する。 if(!cal_start_flag) { if((y < 177) && (db == 3.00)) { // −3.00dB検知 ltostring(6, fx, MsgData1); // バイナリ→ 文字列変換 lcd_Str(18, 7, MsgData1, YELLOW, BLACK); // 周波数表示 lcd_Str(18, 8, "- 3.00dB", YELLOW, BLACK); // -3dB表示 } } // delay_ms(400); // 実測、405mS(これで次回から、0.5+0.1+0.4= 1秒周期のサンプリングになる。) delay_ms(395); // 実測、400mS } EndFlag= 1; if(cal_start_flag) { EndFlag= 0; cal_start_flag= 0; lcd_Str(0, 0, "CAL END ", WHITE, BLACK); } } } /****************************************** * Drawing Coordinate Axis *******************************************/ void AxisDraw(void){ int i; lcd_Clear(BLACK); // X、Y座標軸表示 for(i=0; i<181; i+=45) lcd_Line(i, 17, i, 217, GREEN); // X axis for(i=17; i<218; i+=40) lcd_Line(0, i, 180, i, GREEN); // Y axis lcd_Str(0, 16, "10", CYAN, BLACK); // 10 lcd_Str(3, 16, "100", CYAN, BLACK); // 100 lcd_Str(7, 16, "1k", CYAN, BLACK); // 1k lcd_Str(10,16, "10k", CYAN, BLACK); // 10k lcd_Str(14,16, "100kHz", CYAN, BLACK); // 100kHz // lcd_Str(15, 0, "+10", CYAN, BLACK); // +10 lcd_Str(15, 0, "+10dB", CYAN, BLACK); // +10dB // lcd_Str(15, 3, "0dB", CYAN, BLACK); // 0dB lcd_Str(15, 3, "0", CYAN, BLACK); // 0 lcd_Str(15, 6, "-10", CYAN, BLACK); // -10 lcd_Str(15, 9, "-20", CYAN, BLACK); // -20 lcd_Str(15, 12, "-30", CYAN, BLACK); // -30 lcd_Str(15, 15, "-40", CYAN, BLACK); // -40 } /******************************************************** * Numerical Value-> Ascci Convert (long) *********************************************************/ void ltostring(char digit, unsigned long data, char *buffer) { char i; buffer += digit; // To Last of Strings for(i=digit; i>0; i--) { // From LS. Digit To MS. Digit buffer--; *buffer = (data % 10) + '0'; // This Digit Value-> Ascii Convert ('0'=0x30)-> Store Buffer data = data / 10; // Digit-1 } /// ブランキング処理 i = 0; // buffer++; while((i < digit-1)&&(*buffer == '0')) // 上位桁が0の間 { *buffer = ' '; // ブランクに変換 buffer++; i++; } } /**************************************** * Floatから文字列へ変換 *  合計有効桁は8桁以下とすること ****************************************/ void ftostring(long seisu, long shousu, float data, char *buffer) { long i; long dumy; if(shousu != 0) //小数部桁ありか buffer += seisu+shousu+1; //全体桁数+小数点 else //小数部桁なしのとき buffer += seisu + shousu; //全体桁数のみ buffer--; //配列ポインタ-1 for(i=0; i0; i--) { //小数桁数分繰り返し *buffer =(dumy % 10)+'0'; //数値を文字に変換格納 buffer--; //格納場所下位から上位へ dumy /= 10; //次の桁へ } if(shousu != 0) { //小数桁0なら小数点なし *buffer = '.'; //小数点を格納 buffer--; //ポインタ-1 } for(i=seisu; i>0; i--) { //整数桁分繰り返し *buffer = (dumy % 10)+'0'; //数値を文字に変換格納 buffer--; //ポインタ-1 dumy /= 10; //次の桁へ } /* i = 0; // ブランキング処理 buffer++; while((i