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信号です。デューティーのみ可変です。

PSMC_LED_PWM_01.jpg

Period 1mS (1KHz) Duty 1% (10uS)

PSMC_LED_PWM_02.jpg

Period 1mS (1KHz) Duty 20% (200uS)

PSMC_LED_PWM_03.jpg

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)