今回も前回と同様、
PIC16F1938を使い
3chのADC入力から、3chのLEDのPWM調光を作りました。
今回も、回路図、コード、オシロの波形
ハマったポイントをしっかり載せます。
①今回の回路図

前回の10進ロータリスイッチは残したままにしています。
今回はこれは使いません。
回路のポイントは、
入力
①ボリューム1:RA5 (AN4)
②ボリューム2:RB2 (AN8)
③ボリューム3:RB3 (AN9)
出力
①LED1 : RC2 (CCP1)
②LED2 : RC1 (CCP2)
③LED3 : RB5 (CCP3)
使うのはこれだけです。
ボリューム1を回してLED1の調光のデューティーを変える。
という感じです。
回路はとてもシンプルです。
②今回のコード
c
#include <xc.h>
#include <pic16f1938.h>
#pragma config FOSC = INTOSC
#pragma config PWRTE = OFF
#pragma config WDTE = OFF
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#define _XTAL_FREQ 8000000UL
void PWM_Init(void)
{
OSCCON = 0b01110010; //8MHz
TRISCbits.TRISC2 = 0; //RC2 OUT
TRISCbits.TRISC1 = 0; //RC1 OUT
TRISBbits.TRISB5 = 0; //RB5 OUT
ANSELB = 0x00; //RBxbits digital
APFCONbits.CCP2SEL = 0; //0:RC1, 1:RB3
APFCONbits.CCP3SEL = 1; //0:RC6, 1:RB5
T2CONbits.T2CKPS = 0b10; //prescaler 1:16
T2CONbits.TMR2ON = 1; //Timer2 ON
PR2 = 124; // 1kHz
CCP1CON = 0b00001100; //CCP1 PWM mode
CCP2CON = 0b00001100; //CCP2 PWM mode
CCP3CON = 0b00001100; //CCP3 PWM mode
}
void ADC_Init(void){
//RA5, RB2, RB3を出力ピンに設定
TRISAbits.TRISA5 = 1;
TRISBbits.TRISB2 = 1;
TRISBbits.TRISB3 = 1;
//RA5, RB2, RB3をアナログ有効に設定
ANSELAbits.ANSA5 = 1;
ANSELBbits.ANSB2 = 1;
ANSELBbits.ANSB3 = 1;
ADCON0bits.ADON = 1; //アナログ変換をONに設定
//リファレンス電源をVSSとVDDに設定
ADCON1bits.ADNREF = 0;
ADCON1bits.ADPREF = 0;
ADCON1bits.ADCS = 0b101; //プリスケーラ設定 FOSC/16
ADCON1bits.ADFM = 1; //A/D変換結果の右詰保存
}
void pwm1_set(void){
ADCON0bits.CHS = 0b00100; //AN4(RA5)選択中
__delay_us(10); //コンデンサの充電待ち
ADCON0bits.GO_nDONE = 1; //A/D変換変換中
while(ADCON0bits.GO_nDONE);
unsigned int adc = (unsigned int) ADRESH << 8 | ADRESL;
//adcが4未満は0、1015より大きいとき1023に設定
if(adc < 4) adc = 0;
if(adc > 1015) adc = 1023;
unsigned int duty = (adc * 499UL) / 1023UL;
CCPR1L = duty >> 2;
CCP1CONbits.DC1B = duty & 0x03;
}
void pwm2_set(void){
ADCON0bits.CHS = 0b01000;
__delay_us(10);
ADCON0bits.GO_nDONE = 1;
while(ADCON0bits.GO_nDONE);
unsigned int adc = (unsigned int) ADRESH << 8 | ADRESL;
if(adc < 4) adc = 0;
if(adc > 1015) adc = 1023;
unsigned int duty = (adc * 499UL) / 1023UL;
CCPR2L = duty >> 2;
CCP2CONbits.DC2B = duty & 0x03;
}
void pwm3_set(void){
ADCON0bits.CHS = 0b01001;
__delay_us(10);
ADCON0bits.GO_nDONE = 1;
while(ADCON0bits.GO_nDONE);
unsigned int adc = (unsigned int) ADRESH << 8 | ADRESL;
if(adc < 4) adc = 0;
if(adc > 1015) adc = 1023;
unsigned int duty = (adc * 499UL) / 1023UL;
CCPR3L = duty >> 2;
CCP3CONbits.DC3B = duty & 0x03;
}
void main(void)
{
PWM_Init();
ADC_Init();
while(1)
{
pwm1_set();
pwm2_set();
pwm3_set();
}
}
こんな感じのコードになりました。
③オシロスコープで確認

LED1 : 黄色の波形
LED2 : 青色の波形
LED3 : ピンクの波形
それぞれのボリュームで独自のPWM調光ができました。
全てのボリュームをMAXにし、拡大すると

少し波形が残っていることが確認できました。
dutyを485以上を499にするように
ソフトで書いても波形は変わりませんでした。
④今日の学びと考察
<h3>1. AN8の罠</h3>
ピン名 ≠ レジスタ名
AN8をアナログ設定しようと
c
ANSELBbits.ANSB8 = 1;
と書いていました。
でもコンパイルエラーになる…
今回はここでめっちゃ迷いました。

AN8 は RB2 にあるので、
c
ANSELBbits.ANSB2 = 1;
と書かないとダメでした。
つまり、
PICのアナログ設定は「AN番号」ではなく
「ポート番号(RB2)」に紐づいている
からです。
<h3>2. ADCは"チャンネル切替して1個ずつ読む"</h3>
CCPxビットは5つあるので、
c
ADCON0bits.CHS1 = 0b00100;
ADCON0bits.CHS2 = 0b01000;
ADCON0bits.CHS3 = 0b01001;
みたいに、
同時に5つ設定して同時進行でいけるのかなと、
思っていました。
でも "CHS" しかないので、
読むビットをその都度切替ながら、
読み取って、PWM出力をするのが正解でした。
<h3>3. ADCは"10bit"でもレジスタは"8bit ×2"</h3>
c
unsigned int adc = (unsigned int) ADRESH << 8 | ADRESL;
if(adc < 4) adc = 0;
if(adc > 1015) adc = 1023;
今回は上の様に書いていました。
しかし、下の if文2行が無くてもdutyは動きます。
その時は
c
(unsigned int) ADRESH << 8 | ADRESL;
でもしっかりと動きます。
型をしっかりとキャストして、16bit保存されているので問題ありません。
<h3>4. delay()関数の目的</h3>
pwm_set()関数内にdelay_us(10)を入れているのは、
・スイッチの場合 → チャタリング除去
・ADCの場合 → サンプル保持(コンデンサ充電)
似ているけど違う。
安定するのを待つって意味では一緒かな?
<h3>5. PWM最大でも完全100%ではない?!?!</h3>
ソフト的にはボリュームがMAXの時は、
dutyもMAXになると思っていました。
しかしオシロスコープでは、少し波形が残っています。

かなり拡大して見ることができました。
さらに、同じ回路にも関わらず、波形の形が違って見えました。
考えられる原因:
・PWMの仕様(完全100%にならない)
・分解能の問題
・回路のRC特性
でも確実に波形は違っていました。
完全な100%にするためには、
ボリュームが100%とのときは、
条件分岐をさせて
LATを使って出力させるのがいいと思います。
<h2>まとめ</h2>
・AN番号ではなくポートで設定する
・ADCは1chずつ読む
・PWMは完全100%にはならない
<h2>次回予告</h2>
3ch PWM出力に成功したので、
次は下限のオフセットの設定をしたいと思います。
EEPROMに記憶をさせて~
楽しみです

コメント