---
---
18F14K50のPWM (Pulse Width Moduration) と割込みを使用し、パルス発生器を作成しました。発生させるパルスの精度を高めるために、クロックは高精度の12MHzクリスタルを使用しています。 発生するパルスは、VRで以下の範囲を可変することができます。
繰返し周期 | : 1/8 Hz~1.2MHz 区切りのよい値、49段階 |
デューティサイクル | : 5%~95% 5%毎 |
本来、外部にパルスを出力するには、PICからの出力パルスをFET等でバッファするのですが、私は、ロジック回路の実験が主なので、直接PICの端子から出力しています。ただし、このパルス発生回路は5vで動作させているため、最近増えている 3.3vシステム用に抵抗で3vに分圧した端子も用意しました。
また、乾電池1本から、電源の 5vを発生させるDC-DCコンバータ、HT7750、も組み込み、小型の透明ケースにすべてを納めることができ、気軽に他の実験回路に接続できるようにしています。
繰返し周期により 1/8 Hz~2KHz をLOW Band、 3KHz~1.2MHz をHI Bandとして、2 Bandに分けて処理しています。 LOW Bandは、割込みを利用してパルスを発生させ、HI BandはPWMを利用して発生させています。
文字を画面に表示するLCDライブラリ、AD変換のadcライブラリを利用しています。プロジェクトファイルには、同梱してありますが、このページに記載するのは省略します。
プロジェクトファイル 100_PulseGen_5v.zip |
<プログラム>
//**************************************************************** // PIC18F14K50 信号発生器 <<5v >> 100_PulseGen_5v // Hi-Band 1.2MHz - 3KHz 2011/06/24 // Low-Band 2KHz - 1/8Hz // デューティサイクルは、5%ことに設定できる // 1)Define I/O PORT // PORTB(4)pin13 : SDA // PORTB(6)pin11 : SCL // PORTB(7)pin10 : Band SW デジタル入力 (WPU ON) // PORTC(0)pin16 : LED // AN 9(RC7)pin 9 : VR アナログ入力 デューティサイクル // AN11(RB5)pin12 : VR アナログ入力 周波数 // CCP1(RC5)pin 5 : 信号出力(正論理) // P1B (RC4)pin 6 : 信号出力(負論理) // 2)OSC // 外部クロック 12MHzOSC PLLで48MHz //******************************************************/ // 非安定状態の時LEDを点灯 //******************************************************/ #include <p18f14k50.h> #include <delays.h> #include "lib_adc.h" #include <timers.h> #include <i2c.h> //I2C関数 #include "lib_i2cLCD.h" //I2C接続LCD関数 //-------------- 関数プロトタイプ ------------------------ void DevicesIntlz (void); void CCP_PWM(void); void frq_PWM(void); void itostr(unsigned long, char*, char); void cutNoize(unsigned char); void dutyPWM(void); void CCP_Cmp(void); void frq_Cmp(void); void dutyCmp(void); //-------------- コンフィグレーション --------------------- #pragma config MCLRE = OFF #pragma config PWRTEN = OFF, BOREN = OFF, BORV = 30 #pragma config WDTEN = OFF, WDTPS = 32768 #pragma config STVREN = ON #pragma config FOSC = ECH // 外部クロック #pragma config PLLEN = ON, CPUDIV = NOCLKDIV, USBDIV = OFF #pragma config FCMEN = OFF, IESO = OFF, HFOFST = OFF #pragma config LVP = OFF, XINST = OFF, BBSIZ = OFF #pragma config CP0 = OFF, CP1 = OFF, CPB = OFF, CPD = OFF #pragma config WRT0 = OFF, WRT1 = OFF, WRTB = OFF, WRTC = OFF, WRTD = OFF #pragma config EBTR0 = OFF, EBTR1 = OFF, EBTRB = OFF // #define BandSW PORTBbits.RB7 #define signalOut LATCbits.LATC5 #define LED LATCbits.LATC0 // ----- 割り込みベクトル ----- void isr_high(void); #pragma interrupt isr_high #pragma code h_int_vect = 0x0008 void _h_isr (void){ _asm goto isr_high _endasm } #pragma code // 周波数定数 (Hi Band) ---------------------------------------------------------- rom const unsigned int frqH[22]={3,5,6,10,12,15,20,30,40,50, 60,80,100,150,200,300,400,500,600,800,1000,1200}; rom const unsigned char prT2[22]={249,149,124,74,249,199,149,99,74,59, 199,149,119,79,59,39,29,23,19,14,11,9}; rom const unsigned char psT2[22]={16,16,16,16,4,4,4,4,4,4, 1,1,1,1,1,1,1,1,1,1,1,1}; // 周波数定数 (LowBand) ---------------------------------------------------------- rom const unsigned int frqL[]={-8,-5,-4,-2,1,2,3,4,5,6,8,10,20,30,40,50,60, 80,100,200,300,400,500,600,800,1000,2000}; rom const unsigned int CCPR[]={59999,37499,29999,14999,7499,37499,24999, 18749,14999,12499,9374,59999,29999,19999,14999,11999,9999, 7499,5999,2999,1999,1499,1199,999,749,599,299}; rom const unsigned char psT1[]={10,10,10,10,10,8,8,8,8,8,8,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1}; // ***** 共通変数の定義 ***** char msgHz[] =" Hz "; char msgKHz[] =" KHz"; char msgSlow[] =" 1/ Hz "; char msgFreq[] =" Freq"; char msgPerC[] =" %"; char msgDuty[] =" Duty"; unsigned char fqCmd; // 現、周波数 unsigned char dcCmd; // 現、DutyCycle unsigned char VRpos; // VR position unsigned char bdCmd; // 周波数帯 Hi:1、Low:0 unsigned char inCmd; // 入力指示 unsigned char fqVRe; // VR 期待位置 周波数 unsigned char dcVRe; // VR 期待位置 Duty unsigned char refsh = 1; // 表示の再描画 unsigned char dcCnt = 0; // インタラプトの発生回数 unsigned char dcLow = 10; // Lowになるタイミング unsigned char dcMax = 20; // 一周期に必要なインタラプト数 // ****** 割り込み処理 **************************** // dcCnt 20回または200回で1周期 // 0 : 計測準備完了 // 1 - dcLow : HI // dcLow - dcMax : LOW // ************************************************ void isr_high(){ if (PIR1bits.CCP1IF == 1) { // CCPからの割り込みか PIR1bits.CCP1IF = 0; // CCP割込フラグをOFF if(dcCnt == 0){ // 信号をHIにする Nop();Nop();Nop(); // タイミングを合わせる遅延 Nop();Nop(); LATC = 0b00100000; }else if(dcCnt == dcLow){ // 信号をHIにする LATC = 0b00010000; } if( ++dcCnt >= dcMax) dcCnt = 0; // 一周期終了 } } //*************** メイン関数 *************** void main(void){ PORTA = 0x00; PORTB = 0x00; PORTC = 0x00; TRISA = 0b11111111; //PortA すべて入力 TRISB = 0b11111111; //PortB すべて入力 TRISC = 0b11001110; //PortC RC5(CCP1),RC4(P1B)は出力他は入力 ANSEL = 0b00000000; //AN11、AN 9はアナログ 他はデジタル ANSELH = 0b00001010; // ↑ WPUA = 0b00000000; //RB7だけWeak Pull Upを行う WPUB = 0b10000000; // ↑ INTCON2bits.NOT_RABPU = 0; // ↑ DevicesIntlz(); // 内部デバイスの初期化 bdCmd = 1; // Hi Bandnに仮設定 CCP_PWM(); // CCPをPWMモードにする refsh = 1; // 再セットを行う // ---------------- LCD 設定 --------------------- LCD_int(); // LCDを初期化 LCD_str(msgFreq); // LCD上段に初期値表示 LCD_posyx(1,0); // LCD表示位置指定 LCD_str(msgDuty); // LCD下段に初期値表示 while(1){ // ++++++++++ 周波数帯 確認 ++++++++++ inCmd = BandSW; if(bdCmd != inCmd){ // BandSWが操作されたら LED = 1; // 非安定を点灯 bdCmd = inCmd; // BandCommandを変更し refsh = 1; // 再セットを行う if(bdCmd==1){ // Hi Band なら CCP_PWM(); // CCPをPWMモード INTCONbits.PEIE = 0; // 割込を不許可 INTCONbits.GIE = 0; }else{ // Low Band なら CCP_Cmp(); // CCPをConpareモード INTCONbits.PEIE = 1; // 割込を許可 INTCONbits.GIE = 1; } } // ++++++++++ 周波数 設定 ++++++++++ SetChanADC(ADC_CH11); // 周波数VR選択 ConvertADC(); // AD変換開始 while(BusyADC()); // AD変換終了待ち VRpos = ADRESH; // AD変換結果取得 cutNoize(fqVRe); // ノイズ除去 if(bdCmd==1)frq_PWM(); // Hi Band PWMモードで設定 else frq_Cmp(); // Low Band Conpareモードで設定 // ++++++++++ デューティ 設定 ++++++++++ SetChanADC(ADC_CH9); // デューティサイクルVR選択 ConvertADC(); // AD変換開始 while(BusyADC()); // AD変換終了待ち VRpos = ADRESH; // AD変換結果取得 cutNoize(dcVRe); // ノイズ除去のため閾値から遠ざける inCmd = ((int)VRpos*19/256+1)*5; // DutyCycleの計算 if ((inCmd != dcCmd)||(refsh == 1)){ // -- 変更された -- LED = 1; // 非安定警告を点灯 refsh = 0; // 再セットFlag OFF dcCmd = inCmd; // DutyCycleの変更 dcVRe = (int)(dcCmd/5-1)*256/19+8; // 指示の中心VR位置計算 // --------- デューティ値表示 ---------- itostr(dcCmd, msgPerC, 2); // 値を文字列に変換 LCD_posyx(1,9); // LCD表示位置指定 LCD_str(msgPerC); // 表示 if(bdCmd==1)dutyPWM(); // Hi Band PWMモードで設定 else dutyCmp(); // Low Band Conpareモードで設定 } // ++++++++++++++++++++++++++++++ LED = 0; // 非安定警告を消灯 }; } //********** デューティサイクルの設定 PWM ******* void dutyPWM(){ unsigned int dutyReg; dutyReg = ((int)prT2[fqCmd]+1) << 2; // PR2から周期を取得 dutyReg = (long)dutyReg * dcCmd/100; // CCPの設定値を計算 CCP1CONbits.DC1B0 = dutyReg & 1; // 下位2ビットの設定 CCP1CONbits.DC1B1 = (dutyReg >> 1) & 1; // ↑↑↑↑ CCPR1L = dutyReg >> 2; // 上位8ビットの設定 } //************ デューティサイクル設定 Cmpare ***** void dutyCmp(void){ if (psT1[fqCmd] > 8){ // psT1が8を超える場合 dcLow = dcCmd * 2; // インタラプト200回で1周期 dcMax = 200; }else{ // それ以外は dcLow = dcCmd / 5; // インタラプト 20回で1周期 dcMax = 20; } } //****** 正整数を文字列に変換する関数 (ゼロサプレス)******** // num 変換する整数 // strAdd 変換結果の格納される文字列 // digit 変換される文字数 //************************************************************ void itostr(unsigned long num, char *strAdd, char digit){ char i; char flag=1; //ゼロサプレス Flag char coma=0; //コンマ Flag strAdd += digit; // 文字列の最後 for(i=digit; i>0; i--) { // 下位桁から処理 strAdd--; // 1桁上に if (flag == 1){ if (coma++ == 3){ //コンマ の位置か *strAdd = ','; //コンマ を格納 coma=0; }else{ // 1桁の数値を文字で格納 *strAdd = (num % 10) + 0x30; num = num / 10; // 次桁の準備 if (num == 0) flag=0; // ゼロなら以降は抑制 } }else{ // 上位の0は抑制する *strAdd = 0x20; // 0の代わりにスペース格納 } } } // ************ CCPの 設定 PWM *********** void CCP_PWM(void){ PIE1bits.CCP1IE = 0; // 割り込みを使用しない。 PIR1bits.CCP1IF = 0; PSTRCON = 0b00000011; // PWM出力がP1A P1Bピン CCPR1L = 0x7E; // 仮の値 CCP1CON = 0b00001101; // PWMモード、Single Output、P1A:正 P1B:負 } // ************ CCPの 設定 Compare *********** void CCP_Cmp(void){ CCP1CON = 0b00001011; // Compare モードspecial event CCPR1L = 0xFF; // 仮の値 CCPR1H = 0xFF; // PIE1bits.CCP1IE = 1; // 割り込みを使用する。 PIR1bits.CCP1IF = 0; } // ************ 内部デバイス初期化 *********** void DevicesIntlz(void){ // -------------------- I2C 設定 --------------------- OpenI2C(MASTER, SLEW_ON); //I2Cマスターモード指定 SSPADD = 0x1D; //Board Rate 400kHz // -------------------- ADC 設定 --------------------- OpenADC(ADC_FOSC_64 & ADC_LEFT_JUST & ADC_12_TAD, ADC_REF_VDD_VDD & ADC_REF_VDD_VSS, ADC_CH11 & ADC_INT_OFF ); // -------------------- Timer 1 設定 --------------------- OpenTimer1( TIMER_INT_OFF & T1_8BIT_RW & T1_SOURCE_INT & T1_PS_1_8 & T1_OSC1EN_OFF & T1_SOURCE_CCP ); PIR1bits.TMR1IF = 0; TMR1H = 0; TMR1L = 0; // -------------------- Timer 2 設定 --------------------- OpenTimer2( TIMER_INT_OFF & T2_PS_1_4 & T2_POST_1_1 ); PIR1bits.TMR2IF = 0; TMR2 = 0; PR2 = 0xFF; } //****************************************** // AD変換のノイズで、コマンドが変動しないよう // AD変換値をコマンド中心値に近づける //****************************************** void cutNoize(unsigned char VRexp){ if(VRpos > VRexp){ if(VRpos > 2) VRpos -= 2; }else{ if(VRpos < 0xFE) VRpos += 2; } } //******** PWM 周波数設定 ******************* void frq_PWM(void){ inCmd = ((int)VRpos * 22)/256; // 周波数番号の計算 if ((inCmd != fqCmd)||(refsh == 1)){ // -- 変更された -- LED = 1; // 非安定警告を点灯 fqCmd = inCmd; // 周波数番号の変更 fqVRe = fqCmd*256/22+5; // 指示の中心VR位置を計算 itostr(frqH[fqCmd], msgKHz, 5); // 表示周波の取得 LCD_posyx(0,6); // LCD表示位置指定 LCD_str(msgKHz); // LCD下段に周波数 switch (psT2[fqCmd]){ // プリスケラの設定 case 1: T2CON = 0b00000100;break; // x1 case 4: T2CON = 0b00000101;break; // x4 default:T2CON = 0b00000111; // x16 } PR2 = prT2[fqCmd]; // 周期プリセットの設定 dutyPWM(); // DutyCycleの更新 } } //******** Compare 周波数設定 ******************* void frq_Cmp(void){ int fqDsp; inCmd = ((int)VRpos * 27)/256; // 周波数番号の計算 if ((inCmd != fqCmd)||(refsh == 1)){ // -- 変更された -- LED = 1; // 非安定警告を点灯 fqCmd=inCmd; // 周波数番号の変更 fqVRe = fqCmd*256/27+5; // 指示の中心VR位置を計算 fqDsp = frqL[fqCmd]; // ---- 周波数の表示 ---- LCD_posyx(0,6); // LCD表示位置指定 if(fqDsp<0){ // 1Hz未満の周波数 fqDsp = - fqDsp; // 1Hz未満は負で示す itostr(fqDsp, msgSlow+4, 1); // 表示 LCD_str(msgSlow); }else{ // 1Hz以上の周波数 itostr(fqDsp, msgHz, 5); // 表示周波の取得 LCD_str(msgHz); // 表示 } // プリスケラの設定 if (psT1[fqCmd]==1)T1CON = 0b00000001; // x1 else T1CON = 0b00110001; // x8 CCPR1H = CCPR[fqCmd] >> 8; // CCPプリセットの設定 CCPR1L = CCPR[fqCmd] & 0xFF; dutyCmp(); // Dutyの設定 } } // --- Copyright (C) 2011-2012 Kazuo Iwamoto All Rights Reserved. ---