akira.kei が 2025年02月02日01時08分16秒 に編集
初版
タイトルの変更
8ピンPICのPIC16F18313を使う(その9)ついにLEDを接続
タグの変更
PIC
PIC16F18313
記事種類の変更
製作品
Lチカの変更
ライセンスの変更
(GPL-3.0+) GNU General Public License, version 3
本文の変更
[<前の記事](https://elchika.com/article/8faa0f63-913d-4897-8778-4f9fc757f5fb/) : [次の記事>]() ## 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) { } } ``` ## できた ブレッドボード上に組んてみて、ボリュームを上げたり下げたりすると明るさが変化することが確認できた。よくできました。 