akira.keiのアイコン画像
akira.kei 2025年02月02日作成 (2025年02月09日更新) © GPL-3.0+
製作品 製作品 Lチカ Lチカ 閲覧数 139
akira.kei 2025年02月02日作成 (2025年02月09日更新) © GPL-3.0+ 製作品 製作品 Lチカ Lチカ 閲覧数 139

8ピンPICのPIC16F18313を使う(その9)ついにLEDを接続

<前の記事 : 次の記事>

ADCでLEDの明るさをコントロール

ついにLEDを接続してみたのだが、PWMのデューティでその明るさをコントロールする。今回はブレッドボードに組んだが今後作る予定の製作品は3灯であることから、PWMを3chほど用意した(PWM5、PWM6およびCCP1)。そのうち3灯を異なるパターンで点けることも考えるが、今回は3灯とも同じ明るさで光らせる。
デューティはADCにボリュームを接続して電圧(0〜5V)を入力し、それを0〜100%のデューティに変換して各PWMに設定する。
Duty:10%
Duty:90%

オシレータは

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) { } }

できた

ブレッドボード上に組んてみて、ボリュームを上げたり下げたりすると明るさが変化することが確認できた。よくできました。
低Duty

実装例

老眼で見えにくくなった IC表面のラベル確認用のUSBカメラの照明に適用してみた。まぁ下(バックグラウンド)が白いと照明がなくても映るんだけどw
実装例

akira.keiのアイコン画像
機械系エンジニアだが電子工作を趣味としている。週末はひとりバーベキュー。
ログインしてコメントを投稿する