PIC PSMCでLEDを調光する
投稿日 2017/01/07
PIC 16F1783のPWMモジュール PSMCでLEDストリングを調光してみました。LEDドライバはオン・セミコンダクターのCAT4238です。10個のLEDを直列にしたストリングを点灯させました。CAT4238によるLEDストリングの点灯については以下をご参照ください。
参考記事: CAT4238 LEDドライバ
このCAT4238にはコントロール端子(SHDN)があります。この端子をLOWレベルにすると消灯。HIGHレベルにするとフルで点灯します。このLOW/HIGHを高速に切り替え、HIGHレベルの割合を変えることにより、調光することができます。つまりPWM信号で調光します。
今回はPWMの周波数を1KHzとし、デューティーを変えて調光してみました。周波数は目で見てちらつかない程度でよいのですが、PSMCの設定にも限界があるので1KHzとしました。低い方は1KHzあたりが限界です。周波数をもっと高くしてもかまいませんが、制御精度(分解能)が粗くなります。
PIC 16F1783のPSMCは様々なPWM信号を作れますが、LEDの調光は単純ですので、もっともシンプルな単相PWM信号を発生させます。
PICのPSMCによる単相PWM信号の発生については以下をご参照ください。
参考記事: PICのPSMCを試してみる 単相PWM
デューティーの調整はいろいろやり方があります。ポテンショメータで分圧した電圧を、ADコンバータで読み込み、その値に応じたデューティー値とする方法。この方法は連続で可変できます。もうひとつはスイッチ入力による方法です。この方法はステップ設定になりますが、安定したやり方です。
PICのPSMCで1KHzのPWM信号を生成する場合、カウンタの値を64000に設定します。デューティー値に32000を設定するとデューティー50%で、LOWレベルが0.5mS、HIGHレベルが0.5mSで丁度半々となります。デューティーの設定幅が64000もの細かさで設定できるわけです。これをスイッチで可変するとなると大変ですので、スイッチを一回押すと+500または-500などとして、ステップ設定とします。
今回は簡単のためスイッチ設定としました。
PICはブレッドボード上で配線し、LEDとLEDドライバーは簡易なものですが基板の上に組んでいます。スイッチは黄色がDuty UP(+500)と緑色がDuty DOWN(-500)です。黄色を押すと明るく、緑を押すと暗くなります。(赤と青は未使用) PWM信号はオシロで観測しています。PSMCのPWM信号はPSMC1A端子のみを使用しています。PWM信号は周期(priod) 1mS(1KHz)の単相PWM信号です。デューティーのみ可変です。
Period 1mS (1KHz) Duty 1% (10uS)
Period 1mS (1KHz) Duty 20% (200uS)
Period 1mS (1KHz) Duty 50% (500uS)
LEDはデューティーを可変することによりきれいに調光できます。ただ、人間の目の特性も影響してか?デューティーを順次ステップアップしていっても、リニアには変化しているようには見えません。スムーズで反応のよい調光にするためには一工夫必要なようです。
プログラム・コード
開発環境
microchip社 MPLAB X IDE v3.45, XC8コンパイラ v1.38
Writter pickit3
//PIC 16F1783
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config CLKOUTEN = OFF
#pragma config IESO = ON
#pragma config FCMEN = ON
// CONFIG2
#pragma config WRT = OFF
#pragma config VCAPEN = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = LO
#pragma config LPBOR = OFF
#pragma config LVP = OFF
#define _XTAL_FREQ 1000000
unsigned int period;
unsigned int duty;
#define PERIOD (64000 / period - 1)
void psmc1_init(void){
PSMC1CONbits.P1DBFE = 0;
PSMC1CLKbits.P1CPRE = 0;
PSMC1CLKbits.P1CSRC = 1;
PSMC1OENbits.P1OEA = 1;
PSMC1STR0bits.P1STRA = 1;
PSMC1PHSbits.P1PHST = 1;
PSMC1DCSbits.P1DCST = 1;
PSMC1PRSbits.P1PRST = 1;
period = 1; //1KHz
PSMC1PRH = PERIOD >> 8;
PSMC1PRL = PERIOD & 0xFF;
duty = PERIOD / 2; //50%
PSMC1DCH = duty >> 8;
PSMC1DCL = duty & 0xFF;
PSMC1PHH = 0 >> 8;
PSMC1PHL = 0 & 0xFF;
PSMC1INT = 0;
PSMC1CONbits.PSMC1EN = 1;
PSMC1CONbits.PSMC1LD = 1;
}
void change_duty(void){
PSMC1DCH = duty >> 8;
PSMC1DCL = duty & 0xFF;
PSMC1CONbits.PSMC1LD = 1;
}
void UP_switch(void) {
duty+= 500;
if(duty > PERIOD / 2) duty = PERIOD / 2;
change_duty();
while (!PORTBbits.RB3);
__delay_ms(25);
}
void DOWN_switch(void) {
duty-= 500;
if(duty < 0) duty = 0;
change_duty();
while (!PORTBbits.RB2);
__delay_ms(25);
}
void main(void) {
TRISA = 0;
LATA = 0;
TRISB = 0;
TRISBbits.TRISB2 = 1; //DOWN SW
TRISBbits.TRISB3 = 1; //UP SW
LATB = 0;
TRISC = 0;
LATC = 0;
ANSELA = 0x00;
ANSELB = 0x00;
APFCON = 0x00;
OSCCON = 0b01011010; //1MHZ_HF
OSCSTAT = 0x00;
OSCTUNE = 0x00;
psmc1_init();
while (1) {
if (!PORTBbits.RB3) UP_switch();
if (!PORTBbits.RB2) DOWN_switch();
}
}
(JF1VRR)