PIC 12F615


デジタル調光器に挑戦 (2)

いざプログラム

前回は100単位時間で考えていましたが、デジタルっぽく、きりの良い値を使いたいと思います。そうです。64です。
1周期を64単位時間にして、0~64の65周期で実装を試みます。

/*
 * CNC Power unit
 * 
 * File:   main.c
 * Author: masato
 *
 * Created on 2017/02/05, 8:02
 */

// PIC12F615 Configuration Bit Settings
// 'C' source line config statements
// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config IOSCFS = 8MHZ    // Internal Oscillator Frequency Select (8 MHz)
#pragma config BOREN = NSLEEP   // Brown-out Reset Selection bits (BOR enabled during operation and disabled in Sleep)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

static unsigned char cIntCount = 0;
static unsigned char cPeriodCount = 0;
typedef struct {
    unsigned char old   : 1;
    unsigned char odd   : 1;
    unsigned char onoff : 1;
    unsigned char slope : 1;
    unsigned char limit : 3;
} FLAG;
static FLAG stFlag = {0, 0, 0, 0, 0};
static unsigned char cSwitch[8] = {0, 0, 0, 0, 0, 0, 0, 0};

// 電源をOn Offするタイミングを作成
void setPowerSwitch() {
    cSwitch[0] = 0x00;
    cSwitch[1] = 0x00;
    cSwitch[2] = 0x00;
    cSwitch[3] = 0x00;
    cSwitch[4] = 0x00;
    cSwitch[5] = 0x00;
    cSwitch[6] = 0x00;
    cSwitch[7] = 0x00;
    switch (cPeriodCount & 0b01111111) {
        case 64:
            cSwitch[0] |= 0x80;
        case 63:
            cSwitch[4] |= 0x80;

            (中略)

        case  2:
            cSwitch[0] |= 0x40;
        case  1:
            cSwitch[4] |= 0x40;
    }
}

// 電源のOn Off
void powerSwitch() {
    GP0 = (cSwitch[(cIntCount & 0b00111000) >> 3] & (0b10000000 >> (cIntCount & 0b00000111))) > 0;
}

// 割り込み処理
void interrupt isr(void) {
    // AC同期割込み
    if (INTCONbits.INTF) {
        if (stFlag.odd) {
            if (!(cIntCount & 0b00111111)) {
                if (stFlag.slope) {
                    setPowerSwitch();
                    if (stFlag.onoff) {
                        if ((((cPeriodCount & 0b01111000) >> 3) > stFlag.limit) || (stFlag.limit == 0))
                            stFlag.slope = 0;
                        else
                            cPeriodCount++;
                    }
                    else {
                        if (!cPeriodCount)
                            stFlag.slope = 0;
                        else
                            cPeriodCount--;
                    }
                }
                else if (!cPeriodCount)
                    // 通電終了に伴って同期停止
                    INTCONbits.INTE = 0;
            }
            powerSwitch();
            cIntCount++;
        }
        stFlag.odd = !stFlag.odd;
        INTCONbits.INTF = 0;    // 割り込みフラグクリア
    }
    // 状態変化割り込み
    if (INTCONbits.GPIF) {
        if (stFlag.old != GP3) {
            stFlag.old = !stFlag.old;
            stFlag.onoff = !stFlag.onoff;
            stFlag.slope = 1;
            if (stFlag.onoff)
                cPeriodCount = 0;
            cIntCount = 0;
            // AC 同期開始
            INTCONbits.INTE = 1;
        }
        INTCONbits.GPIF = 0;    // 割り込みフラグクリア
    }
}

void main(void) {

    // レジスタセット
    OPTION_REGbits.nGPPU =  1;  // GPIO pull-ups are disabled
    OPTION_REGbits.INTEDG = 0;  // Interrupt on falling edge of INT pin
    ANSEL =  0b00000000;        // Analog Select
    TRISIO = 0b00111110;        // GPIO Tri-State Control
//  TRISIObits.TRISIO0 = 0;         // 信号出力
//  TRISIObits.TRISIO1 = 1;         // 出力リミット値 bit 1
//  TRISIObits.TRISIO2 = 1;         // ACクロック入力
//  TRISIObits.TRISIO3 = 1;         // 主幹信号入力
//  TRISIObits.TRISIO4 = 1;         // 出力リミット値 bit 2
//  TRISIObits.TRISIO5 = 1;         // 出力リミット値 bit 3
    CMCON0 = 0b00000000;        // Comparator Control
    WPU =    0b00000100;        // Weak Pull-up
    IOC =    0b00001000;        // nterrupt-on-change

    // 初期化
    GP0 = 0;
    stFlag.old = GP3;
    stFlag.onoff = 0;
    stFlag.slope = 0;
    cPeriodCount = 0;

    // 閾値取得
    stFlag.limit = (GP1 << 2) | (GP4 << 1) | GP5;

    INTCONbits.GPIF = 0;        // 割り込みフラグクリア
    INTCONbits.INTF = 0;
    INTCONbits.GPIE = 1;        // GPIO変更割り込み許可
    INTCONbits.GIE =  1;        // グローバル割り込み許可

    while(1) {
        if (!stFlag.onoff && !stFlag.slope)
            SLEEP();
        NOP();
    }

    return;
}

setPowerSwitch()関数で、On/Offのデータを合成しています。switch case構文で、breakしていないのは意図的に行っています。breakしないことで、複数のビットがセットできます。
powerSwitch()関数で、単位時間ごとにOn/Offを行っています。

ACとの同期

単相交流100Vをそのままマイコンの信号線につなぐことはできませんので、フォトカプラを介してデジタルの信号に変換することにします。
今回使用するフォトカプラは、PS2505-1を使用します。このフォトカプラは、AC入力に対応しています。
sin波 左図のような単相交流の電圧変化は、フォトカプラを介して
タイミング信号 左図のような直流の信号(電圧変化)に変換されます。
このフォトカプラは、AC入力に対応しているためか、入力に極性がありません。そのため、電位差が小さいときに出力が0になり、大きいときに1になります。
これは、1周期で2回信号が発生することを意味しています。そのため、2回に1回処理が実行されるようにしています。
次に割り込みをかけるタイミングについて検討したいと思います。
ソリッド・ステート・リレーの性質によって、ACの電位差が大きいときに出力のACをOn/Offする信号を変化させてもすぐに出力には反映されず、電位差が0になるまで遅延します。
実際の動作には関係ないのかもしれませんが、遅延は最小限に抑えたいと思います。そこで、フォトカプラを介して得られる同期信号の立ち下りでマイコンに対して割り込みをかけることにします。
仮に立ち上がりで割り込みをかけると、約1/2周期の遅延が発生することになります。もしもマイコンの処理が遅かった場合は、立ち上がりで割り込みをかけたほうが、発生する遅延が少なくなります。

最大通電時間

ディップスイッチで8段階の最大通電時間を設定するために、マイコン内のPull-up機能を停止しておきます。
手元のディップスイッチはOn/Offの2つの状態しかありません。仮にPull-up機能が有効になっていると、ディップスイッチがOffの時にマイコンに入る信号は1となり、操作とプログラム及び回路のロジックと相反する状況になってします。この状況は好ましいものではありません。
そのため、ディップスイッチがOffの時にマイコンに入る信号は0となるようにPull-up機能を停止して、回路を設計するようにします。