/****************************************************************************** * PIC18F14K50 * AD9834_AF_OSC_V2 * * <初期バージョンからの変更点> *  ※ 通常の、低周波オシレータ機能(NORMALモード)の他に *    スイープ・モードを追加 *  @ 2つのモードの切替えは、電源投入時の、モードピン(空ポートRB7を使用) *    の状態を読込み、各処理に分岐する。(途中の切替えは考えない。) *  A 通常モード時の、Step SWを、スイープ・モードでは、Start SWとして *    使用する。 *    Start SWが押されたら、現RC2の、LEDポートに追加したピンヘッダより、 *    正の同期パルスを出力後、スイープを開始する。 *    途中のストップは考えない。 *    スイープが終了したら、再び、Start SW ON待ちとし繰返す。 *    * <スイープ仕様> *  @ スイープ周波数範囲= 10Hz〜 100kHz(フラット領域を使う) *  A 周波数ステップ *    周波数帯により、次のステップで周波数を変化させる。 *    10〜 100Hz= 5Hzステップ *    100〜 1kHz= 50Hzステップ *    1k〜 10kHz= 500Hzステップ *    10k〜 100kHz= 5kHzステップ *  B 周波数出力時間= 各1秒とする。(全73ポイント、1スイープ1分13秒) * * Strawberry Linux社の「AD9834 小型 DDS モジュールキット」使用 * * LCD unlook busy : not switch direction (write fixed) * * Condition: * 内蔵発振器:16MHz使用 * Tcy= 1/(16M/4)= 250nS * * 2015/9/3 * 作成:N.Ishii ********************************************************************************/ #include #include "LCD_Lib_PIC18F.h" #include "delays.h" #pragma config FOSC = IRC, PLLEN = ON, FCMEN = OFF #pragma config IESO = OFF, USBDIV = OFF, CPUDIV = NOCLKDIV #pragma config PWRTEN = OFF, BOREN = OFF, WDTEN = OFF //#pragma config HFOFST = OFF, MCLRE = OFF // RA3ポートをマスターリセットピンとして使う。 // 入力ポートとして使う時は、上記のように、MCLRE = OFFとでよいが、 // 下の様に修正しないと、外部RESET SWが、効かない。(MCLREが掛らない。) 150903 #pragma config HFOFST = OFF, MCLRE = ON #pragma config STVREN = ON, BBSIZ = OFF, LVP = OFF #pragma config XINST = OFF #pragma config CP0 = OFF, CP1 = OFF, CPB = OFF #pragma config WRT0 = OFF, WRT1 = OFF, WRTB = OFF, WRTC = OFF #pragma config EBTR0 = OFF, EBTR1 = OFF, EBTRB = OFF #define SYNC LATCbits.LATC2 // 同期信号 #define MODE PORTBbits.RB7 // モード設定ピン #define SW3 PORTBbits.RB6 // Step/Start SW /// ロータリエンコーダー #define RE_A PORTBbits.RB4 #define RE_B PORTBbits.RB5 /// DDSコントロールポート定義 #define DDS_FSYNC LATCbits.LATC3 // AD9834 sync #define DDS_SCLK LATAbits.LATA5 // AD9834 clock #define DDS_SDATA LATAbits.LATA4 // AD9834 data /// Message // 可変メッセージ char MsgFrq[] = "Frq = xxxxxxHz"; // 固定メッセージ char MsgDefFrq[] = "Frq = 1000Hz"; char MsgS1[] = "Step = 1Hz"; char MsgS10[] = "Step = 10Hz"; char MsgS100[] = "Step = 100Hz"; char MsgS1K[] = "Step = 1000Hz"; char MsgS10K[] = "Step = 10000Hz"; char MsgS100K[] = "Step = 100000Hz"; // スイープ機能用メッセージ char MsgSwMode[] = "Sweep Mode "; char MsgSwStart[] = "Sweep Start "; char MsgSwEnd[] = "Sweep End "; unsigned long FrqSet= 1000; unsigned long StepSet= 1; char ChangeFlag= 0; char SwCount= 1; /// スイープ機能用変数 unsigned long F_Start= 10; unsigned int F_Step= 5; int Step_Count; /// 関数プロトタイプ宣言 void dds_dataset(unsigned long data, unsigned int bt); void freq_to_serial(unsigned long f); void ltostring(char digit, unsigned long data, char *buffer); /// 割込み宣言(優先順位を使わない、1レベル高位割込み) #pragma interrupt isr /// 割込みベクタへジャンプ命令セット #pragma code isrcode = 0x0008 // 割込みベクタ宣言 void isr_direct(void) { _asm goto isr _endasm } #pragma code /**************************************************************** * 割込み処理関数(RE_A状態変化割込み) * A相の立下りを基準に、B相のレベルを読込んで回転方向を判断 * *<説明> * 電源ON時に、何もしなくてもRE出力が、立上る(両接点オープン時) * ことがあるので、A相の立上りで割込みが入ってしまう。 * 電源ON時に、何もしなくてもRE出力が、Lo(両接点クローズ時) * の場合は、立下がることはないので余分な割込みは入らない。 * そのために、A相の立下りを基準に、判断している。 *****************************************************************/ void isr(void) { if(INTCONbits.RABIF) { // 割込み発生? // LED= 1; delay_ms(5); // チャッタ回避 /// RE読込み if(!RE_A) { // A相の割込みが入った時、A相が、Loか?(つまり立下りか?) if(RE_B) { // 時計回り(B相が、Hi)だったら、インクリメント FrqSet += StepSet; if(FrqSet > 500000) FrqSet= 500000; } else { /// 反時計回り(B相が、Lo)だったら、ディクリメント FrqSet -= StepSet; if(FrqSet < StepSet) FrqSet= StepSet; } ChangeFlag= 1; } } INTCONbits.RABIF = 0; } /***************** * メイン関数 ******************/ int main(void) { OSCCON = 0b11111111; // 16MHz internal clock UCONbits.USBEN = 0 ; // USBは使用しない /// REピン(RB4,5)は、アナログ入力と併用なので /// ANSELxビットを'0'にリセットしデジタルピンとして使用するように設定 ANSELHbits.ANS10 = 0; // RB4 digital input ANSELHbits.ANS11 = 0; // RB5 digital input TRISAbits.TRISA4= 0; // RA4(SDATA) is Output TRISAbits.TRISA5= 0; // RA5(SCLK) is Output TRISC= 0; // RC is Output(D7-4, FSYNC, LED, RS. STB) TRISB= 0b11110000; // RB4-7 is Input SYNC= 0; // 同期信号= Lo /// 内部プルアップ設定 INTCON2bits.RABPU = 0; // プルアップ許可 WPUB = 0b11110000; // RB4-7 is Pull-Up //// 割込み関連の初期化 IOCBbits.IOCB4= 1; // RB4(RE_A)を状態割込みで使うことを許可する。 INTCONbits.RABIE = 1; // これで初めて、IOCBビット=1に設定したピンの状態割込みが許可される。 /// DDS初期化の前に、MCLKが安定するまでの待機時間を追加 delay_ms(100); /// DDS制御信号レベル初期化と、DDSリセットコマンド転送 DDS_FSYNC= 1; // FSYNC(CS)は、アクティブローなので、待機時は、'1' DDS_SCLK= 1; // AD9834は、立下りエッジ読込みなので、待機時は、'1' DDS_SDATA= 0; // DATAは、'0'待機 /// DDS RESET(RESETパルス出力だけにする) dds_dataset(0x2100, 16); // RESET ON dds_dataset(0x2000, 16); // RESET OFF /// 液晶初期化 lcd_init(); // LCD初期化 lcd_clear(); // 全消去 /// 電源投入時の、モードピンの状態を読込み、各処理に分岐 if(MODE) { /// 通常モードを実行 /// デフォルト液晶表示 lcd_str(MsgDefFrq); // 1行目に出力周波数設定表示 lcd_cmd(0xC0); // 2行目の先頭に移動 lcd_str(MsgS1); // 2行目にスキップ周波数設定表示 freq_to_serial(1000); // デフォルトDDS出力= 1000Hz // 全体の割込みの許可 INTCONbits.GIE=1; // 割り込みをイネーブルにする /// Main Loop while(1) { /// SW3(Step)読込み delay_ms(10); // チャッタ回避 if(!SW3) { ++SwCount; if(SwCount > 6) SwCount= 1; lcd_cmd(0xC0); switch(SwCount){ case 1: StepSet= 1; lcd_str(MsgS1); break; case 2: StepSet= 10; lcd_str(MsgS10); break; case 3: StepSet= 100; lcd_str(MsgS100); break; case 4: StepSet= 1000; lcd_str(MsgS1K); break; case 5: StepSet= 10000; lcd_str(MsgS10K); break; case 6: StepSet= 100000; lcd_str(MsgS100K); break; default:; break; } while(!SW3); // SWリリース待ち(Hiになるまで待つ) } if(ChangeFlag) { /// DDS出力 freq_to_serial(FrqSet); /// 変数FrqSetの値を、液晶の1行目に表示 ltostring(6, FrqSet, MsgFrq + 8); lcd_cmd(0x80); lcd_str(MsgFrq); ChangeFlag= 0; } } } else { //// スイープ・モードを実行 /// デフォルト液晶表示 lcd_str(MsgSwMode); // 1行目に"Sweep Mode"表示 //// Main Loop while(1) { do { delay_ms(5); // チャッタ回避 }while(SW3); // Start SW ON 待ち /// 同期パルス出力 SYNC= 1; delay_ms(1); SYNC= 0; while(F_Start != 100000) { /// スイープ中だったら実行(1つ手前の、95000Hzまで実行中) /// 各周波数帯での、DDSステップ出力 for(Step_Count= 0; Step_Count < 18; ++Step_Count) { /// 変数F_Startの値を、液晶の2行目に表示 ltostring(6, F_Start, MsgFrq + 8); lcd_cmd(0xC0); lcd_str(MsgFrq); freq_to_serial(F_Start); // 次の、F出力(15, 20, 25・・・) delay_ms(1000); F_Start += F_Step; } F_Step *= 10; // 次の周波数帯の、ステップ数セット(50, 500, 5000) } /// 変数F_Startの値を、液晶の2行目に表示(最終周波数:100000Hz) ltostring(6, F_Start, MsgFrq + 8); lcd_cmd(0xC0); lcd_str(MsgFrq); freq_to_serial(F_Start); // 最終周波数:100000Hz出力(100kHz出力のままでよい。) /// スイープが終了したら、開始周波数とステップ数を初期化 F_Start= 10; F_Step= 5; lcd_cmd(0xC0); // 2行目の先頭に移動 lcd_str(MsgSwEnd); // 2行目に"Sweep End"表示して再度、Start SW 待ちにする。 } } } /************************************************************************** * serial data send (MSB first) * 16ビット(bt=16で指定)SPI転送 * AD9834は、SCLKの立下りエッジでデータを読込む ****************************************************************************/ void dds_dataset(unsigned long data, unsigned int bt) { unsigned int i; DDS_FSYNC = 0; for(i = bt; i > 0 ; i--) { DDS_SDATA = (data >> (i - 1)) & 0x00000001; DDS_SCLK = 0; DDS_SCLK = 1; } DDS_FSYNC = 1; } /***************************************************************** * AD9834 DDS control (frequency to serial code) * 希望の出力周波数から、シリアルコードを算出し、シリアル転送する。 ******************************************************************/ void freq_to_serial(unsigned long f) { unsigned long msb, lsb; unsigned long data; data = f << 2; // 指定周波数を、4倍したのが、周波数レジスタにセットする値になる msb = (data >> 14) + 0x4000; // 上位14bit送信データ+4000H(周波数レジスタアドレス指定(b15=0.b14=1):FREQ0レジ)を、msbにセット lsb = (data & 0x00003fff) + 0x4000;// 下位14bit送信データ+4000H(周波数レジスタアドレス指定(b15=0.b14=1):FREQ0レジ)を、lsbにセット dds_dataset(0x2028, 16); //// 制御レジへの書込み // bit13:B28='1':2つの連続した16bit転送で完全なコントロールワードをロード // bit9:PIN/SW= '0',bit8:RESET= '0':リセットビットをリセット(RESET OFF) // bit5:OPBITEN= '1':SB-OUTピン有効 // bit4:SIGNPIB= '0',bit3:DIV2= '1':SB-OUTに出力(サイン出力と同じ周波数で出力) // bit1:MODEビット= '0':サイン波出力 // dds_dataset(0x2020, 16); // bit4:SIGNPIB= '0',bit3:DIV2= '0':SB-OUTに出力(サイン出力の、1/2周波数で出力) // dds_dataset(0x2038, 16); // bit4:SIGNPIB= '1',bit3:DIV2= '1':コンパレータ方形波をSB-OUTに出力 // dds_dataset(0x2000, 16); // サイン波のみの出力 dds_dataset(lsb, 16); // lsb転送(下位14ビットの周波数レジスタ0のデータを、b15,14のコントロールビットと共に転送) dds_dataset(msb, 16); // msb転送(上位14ビットの周波数レジスタ0のデータを、b15,14のコントロールビットと共に転送) dds_dataset(0xC000, 16); // 位相レジスタ初期化:0 } /******************************************************** * 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++; } }