ADCでLEDの明るさをコントロール
ついにLEDを接続してみたのだが、PWMのデューティでその明るさをコントロールする。今回はブレッドボードに組んだが今後作る予定の製作品は3灯であることから、PWMを3chほど用意した(PWM5、PWM6およびCCP1)。そのうち3灯を異なるパターンで点けることも考えるが、今回は3灯とも同じ明るさで光らせる。
デューティはADCにボリュームを接続して電圧(0〜5V)を入力し、それを0〜100%のデューティに変換して各PWMに設定する。
オシレータは
LED点灯程度のコントロールにはあまり速いクロックは必要ないので、動作クロックは1MHzで十分だ。PWMはどれもTimer2を利用するがPR2レジスタを最大255に設定すると10ビット解像度でPWMデューティ制御ができる。この10ビットはADCの分解能でもあるわけで、ADCの結果をそのままデューティとして書き込めるよう設定する。
#pragma config
でCONFIG1を以下のように設定しておくと特にmainルーチンでオシレータ関連レジスタを設定しなくても1MHz駆動される。
// CONFIG1
#pragma config FEXTOSC = OFF
#pragma config RSTOSC = HFINT1
#pragma config CLKOUTEN = OFF
#pragma config CSWEN = OFF
#pragma config FCMEN = OFF
なお冒頭の図の通りPWM周波数は約1kHzである。LED点灯制御では問題ないが、直流モータ等の制御の際には音が聞こえてしまう可能性が高い。
ADC割り込み
ADCは変換開始を指示してから結果が得られるまである程度の時間がかかるので、変換終了の割り込みをかけて使うことが多い。今回はPWM自体がハードウェアのみで動くので、必ずしも割り込みを使う必要もない(while(1)ループ内でポーリングしても良い)が、ここは割り込みを使うことにする。割り込みルーチンは以下の通り。ルーチンの最後でAD変換をリスタートことでずっとAD変換を続けることになる。
void __interrupt() isr(void) {
if(PIR1bits.ADIF) {
PIR1bits.ADIF=0;
PWM5DCL=ADRESL;
PWM6DCL=ADRESL;
CCPR1L =ADRESL;
PWM5DCH=ADRESH;
PWM6DCH=ADRESH;
CCPR1H =ADRESH;
ADCON0bits.GO=1;
}
}
ADC割り込みフラグを見てそれをクリアし、PWMとCCPのデューティレジスタにADC変換結果をそのまま渡している。ADC結果もCCPデューティも左寄せに設定してあるからだ。
Timer2
PWMもCCPのPWMモードもタイマにはTimer2を使う。入力は元々Fosc/4 固定でプリスケーラも使わないので、単純にスタートさせておくだけでいいが、今回はちゃんと初期化してみた。
void setupTMR2(void) {
T2CON=0;
T2CONbits.T2OUTPS=0b0000;
T2CONbits.TMR2ON=1;
}
PWMとCCP(PWM Mode)
以下にPWMの初期設定ルーチンを示すが、どのモジュールも設定は同じにしてある。データシートには最初に出力ピンは無効にしとけ、とあるので冒頭でTRISAの設定(出力バッファOFF)をしている。また、CCP1FMTビットを1にするとデューティが左寄せになる。
void setupPWM(void) {
TRISAbits.TRISA2=1; // PWM5
TRISAbits.TRISA4=1; // PWM6
TRISAbits.TRISA5=1; // CCP1
RA2PPSbits.RA2PPS=0b00010; //PWM5->RA2
RA4PPSbits.RA4PPS=0b00011; //PWM6->RA4
RA5PPSbits.RA5PPS=0b01100; //CCP1
PR2=255;
PWM5CONbits.PWM5POL=0;
PWM5CONbits.PWM5EN=1;
PWM6CONbits.PWM6POL=0;
PWM6CONbits.PWM6EN=1;
CCP1CONbits.CCP1FMT=1;
CCP1CONbits.CCP1MODE=0b1111;
CCP1CONbits.CCP1EN=1;
TRISAbits.TRISA2=0; // PWM5
TRISAbits.TRISA4=0; // PWM6
TRISAbits.TRISA5=0; // CCP1
}
ADC設定
ADCの初期化ルーチンを以下に示す。RA0ピンはアナログ入力として出力バッファを無効に、ADFMビットを0にすると結果が左寄せになる。割り込み設定(PIR1、PIE1)をしたらAD変換をスタートさせる。
void setupADC(void) {
TRISAbits.TRISA0=1;
ANSELA=0;
ANSELAbits.ANSA0=1; // ADC
ADCON0bits.CHS=0;
ADCON1bits.ADFM=0;
ADCON1bits.ADCS=0b100;
ADCON0bits.ADON=1;
PIR1bits.ADIF=0;
PIE1bits.ADIE=1;
ADCON0bits.GO=1;
}
mainルーチン
ADC割り込みをソフトウェアで処理している以外は全部ハードウェアで実現しているため、Timer2、PWMおよびAD変換をセットアップしたら、割り込み許可を出して空の永久ループを実行する。
void main(void) {
setupTMR2();
setupPWM();
setupADC();
INTCONbits.PEIE=1;
INTCONbits.GIE=1;
while(1) { }
}
できた
ブレッドボード上に組んてみて、ボリュームを上げたり下げたりすると明るさが変化することが確認できた。よくできました。
実装例
老眼で見えにくくなった IC表面のラベル確認用のUSBカメラの照明に適用してみた。まぁ下(バックグラウンド)が白いと照明がなくても映るんだけどw
投稿者の人気記事
-
akira.kei
さんが
2025/02/02
に
編集
をしました。
(メッセージ: 初版)
-
akira.kei
さんが
2025/02/02
に
編集
をしました。
-
akira.kei
さんが
2025/02/09
に
編集
をしました。
ログインしてコメントを投稿する