---
---
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. ---