PICで電飾 PWM調光 その3 ウェーブ
CCPが出てこない
PIC、PWMとググると、CCPモジュールという言葉によく行き当たります。
実はPICに実装されているCCPモジュールには、PWM信号を出力するPWMモードがあって、それを使えばレジスタの設定を書くだけで周期やDuty比が決まり、信号が出力されるので、プログラムは随分と短くてすみます。
しかしここではそれを使わずに来ました。
この機能もなかなかの「初見殺し」で、データシートを読んで、ふんふん、こうすればいいのか、とはなかなかなりません。
ハードルの高さではここまで説明してきた、割り込み関数を使ったプログラム方式と負けず劣らずだと思います。
覚える価値はあると思うので、また余力があれば説明をしたいと思うのですが、実際のところプログラム方式の方が優れている点もあります。
プログラム方式の強み
というのも、PWMモードで出力できるのはそのための機能が割りあてられている足からだけだからです。
12F683 なら1本、16F1827なら4本の足にその機能が割りあてられています。したがって、一度に調光できるLEDの数もそれだけです。
しかしプログラムでPWMを実現すれば、デジタル出力できる足はすべてPWM調光に使えます。
16F1827なら、電源とRA5以外、12F683なら電源とGP3以外どれでも使えるということです。おかげで回路をつくるときの自由度が高くなり、線の引き回しが楽になるという効果があります。
そして1個のPICでLEDを数多く光らせたいならこのプログラム方式は十分にかかる手間に見合います。
LED10個を16F1827に繋いで、
発光の強さを順繰りに変えることで光のウェーブを作る例をお見せします。
こういう電飾がPIC1個で実現できるところも、プログラムによるPWM調光の強みということになると思います。
一応、プログラムも最後に載せておきます。
#include <xc.h>
#define _XTAL_FREQ 8000000
//コンフィグ設定
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
int Counter=0, Duty0=5, Duty1=10, Duty2=15, Duty3=40, Duty4=100, Duty5=40, Duty6=15, Duty7=10, Duty8=5, Duty9=5;//変数と初期値の設定
int Reserve;
//メイン関数ここから
void main(void)
{
//特殊レジスタの設定
OSCCON = 0b01110010 ;//内部クロック8MHz
ANSELA = 0b00000000 ;//RAの足をすべてデジタルI/Oとして使う
ANSELB = 0b00000000 ;//RBの足をすべてデジタルI/Oとして使う
OPTION_REG = 0b00000000;//内部プルアップ抵抗を使う
TRISA = 0b00000000 ;//RAの足はすべて出力に使う
TRISB = 0b00001100 ;//RBの足のうちRB2,RB3を入力に使う
WPUB = 0b00001100 ;//RB2,3に内部プルアップ抵抗をつなぐ
PORTA = 0b00000000 ;//RAの初期値はすべてゼロ
PORTB = 0b00000000 ;//RBの初期値はすべてゼロ
// タイマー2設定
T2CON=0b00000100;//②プリスケール1、③ポストスケール1
PR2=99;//④PR2初期値99に
TMR2IF = 0; //タイマ2割り込みフラグゼロに
TMR2IE=1;//タイマ2割り込み許可
TMR2ON=1;//タイマ2スタート
PEIE=1;//周辺割り込み許可
GIE=1;//全体割り込み許可
while(1)
{
Reserve=Duty9;
Duty9=Duty8;
Duty8=Duty7;
Duty7=Duty6;
Duty6=Duty5;
Duty5=Duty4;
Duty4=Duty3;
Duty3=Duty2;
Duty2=Duty1;
Duty1=Duty0;
Duty0=Reserve;
__delay_ms(10);
}
}//メイン関数ここまで
//割り込み関数ここから
void __interrupt() xxx(void)
{
if(TMR2IF)//タイマ2による割り込み
{
PR2=99;//PR2初期値99にもどす
Counter=Counter+1;//①カウントアップ
if (Counter==100)//②カウント100になったら
{
Counter=1;//②リセットしてカウントを1に
if(Duty0>0)RA1=1;
if(Duty1>0)RA0=1;
if(Duty2>0)RA7=1;
if(Duty3>0)RA6=1;
if(Duty4>0)RB7=1;
if(Duty5>0)RB6=1;
if(Duty6>0)RB5=1;
if(Duty7>0)RB4=1;
if(Duty8>0)RB0=1;
if(Duty9>0)RB1=1;
}
else
{
if (Counter>Duty0) RA1=0;
if (Counter>Duty1) RA0=0;
if (Counter>Duty2) RA7=0;
if (Counter>Duty3) RA6=0;
if (Counter>Duty4) RB7=0;
if (Counter>Duty5) RB6=0;
if (Counter>Duty6) RB5=0;
if (Counter>Duty7) RB4=0;
if (Counter>Duty8) RB0=0;
if (Counter>Duty9) RB1=0;
}
TMR2IF=0;
}
}//割り込み関数ここまで