[BC19-03]3LEDガジェットPIC12F1822

8ピンマイコンPIC12F1822の全てのI/O ピンを使ったマイコンガジェット。

3つのLEDを光らせ、圧電ブザから音階(っぽく聞こえるもの)を鳴らせる。タクトスイッチと光センサで、接触・非接触インタフェースも使える。

回路図

実体配線図


使用部品

PICマイコン PIC12F1822-I/P 1個

LED φ5mm VF1.9から2.9V 程度 3個

カーボン抵抗器 150Ω(茶緑茶金)3個

カーボン抵抗器100kΩ(茶黒黄金)1個

フォトトランジスタ NJL7502L 1個

タクトスイッチ2PIN 1個

積層セラミックコンデンサ 0.1uF (104) 1個

ブレッドボード EIC-801 1個

圧電サウンダ (13mm) Murata PKM13EPYH4

ジャンパ線(φ0.65mm単線)いくらか

ソースコード例


/*
 * File:   pwmled-adc-pwmbuz-12f1822.c
 * Author: KAIMU
 *
 * Created on 2019/11/03
 */

/*
 * Input
 *  RA4/AN3 (PIN2) : photo transistor, bright:0, dark:1
 *  RA3 (PIN4)  : Tact switch , ON:0, OFF:1 , weak pull-up 
 * Output
 *  CCP1 (RA2) [PIN5] : Piezo Speaker
 *  RA0 [PIN7] : LED0 , active High
 *  RA1 [PIN6] : LED1 , active High
 *  RA5 [PIN2] : LED2 , active High
 */

#include <xc.h>
#include <stdint.h>


////// ここから、プログラミングのためのデータや関数を定義する /////


#pragma config FOSC     = INTOSC   // 内部クロック使用。
#pragma config WDTE     = OFF      // ウォッチドッグタイマなし。
#pragma config PWRTE    = ON       // 64ms後にプログラム開始。
#pragma config MCLRE    = OFF      // 外部リセットなし。RA3は入力ピン。
#pragma config CP       = OFF      // コード保護なし。
#pragma config CPD      = OFF      // データ保護なし。
#pragma config BOREN    = ON       // 電源電圧低下時のリセットあり。
#pragma config CLKOUTEN = OFF      // クロック出力不可。
#pragma config IESO     = OFF      // 内部外部クロックなし。
#pragma config FCMEN    = OFF      // FCMなし。


// 動作設定その2
// Configuration 2
#pragma config WRT    = OFF        // 書き込み保護なし。
#pragma config PLLEN  = OFF        // PLLなし。
#pragma config STVREN = ON         // スタックオーバー/アンダーフローでリセット。
#pragma config BORV   = LO         // 電源電圧低下監視閾値はLowモード
#pragma config LVP    = OFF        // 低電圧プログラムなし。


// I/Oポートの定義 (LEDを接続するポートが変わった時はここを変更する)
#define PORT_LED_0 RA0
#define TRISA_LED_0 TRISA0

#define PORT_LED_1 RA1
#define TRISA_LED_1 TRISA1

#define PORT_LED_2 RA5
#define TRISA_LED_2 TRISA5

// Tact Switch のI/Oポートの設定定義
#define PORT_TACT_SW RA3
#define TRISA_TACT_SW TRISA3
#define WPU_TACT_SW WPUA3

// A/D変換用I/Oポートの設定定義
#define PORT_ADC ANSA4
#define TRISA_ADC TRISA4
#define PORT_NO_ADC 3


#define led0on()    (PORT_LED_0 = 1)
#define led0off() (PORT_LED_0 = 0)
#define led0() (PORT_LED_0 ^= 1)

#define led1on()    (PORT_LED_1 = 1)
#define led1off() (PORT_LED_1 = 0)
#define led1() (PORT_LED_1 ^= 1)

#define led2on()    (PORT_LED_2 = 1)
#define led2off() (PORT_LED_2 = 0)
#define led2() (PORT_LED_2 ^= 1)

//#define isswon()   ( PORT_TACT_SW == 0 )
#define isswon()   ( RA3 == 0 )

void wait(volatile uint32_t t)
// Delay
{
    while (t-- > 0) NOP();
}

void initIo(void) {
    //OSCCON = 0b01111010; // PLL disable, 16MHz internal clock
    // OSCCON = 0b01100010; // PLL disable, 8MHz internal clock
    OSCCON = 0b01101010; // PLL disable, 4MHz internal clock
    //OSCCON = 0b01011010; // PLL disable, 1MHz internal clock
    //OSCCON = 0b00111010; // PLL disable, 500kHz internal clock
    //INTEDG = 0; // Interrupt on falling edge of RB0/INT pin
    //INTE = 1; // External Interrupt Enable
    //GIE = 1; //Interrupt Enable
    ANSELA = 0b00000000; //ALL digital port
    CM1CON0 = 0x00; //Comparator disable
    TRISA = 0b00001000; //I/O Direction. RA0-2,4-5 : output, RA3 : Input
    //OPTION_REGbits.nWPUEN = 1; //// weak pull-up disable
    //WPUA = 0b00000000; // weak pull-up disable
    OPTION_REGbits.nWPUEN = 0; //// weak pull-up enable
    WPUA = 0b00001000; // weak pull-up 3 enable
    LATA = 0b00000000; // PORTA Latch Settings
}

// A/D変換関数

void initAdc(void) {
    PORT_ADC = 1; // PORT_ADCで定義されたポートをアナログポートにする。
    TRISA_ADC = 1; // A/D変換端子を入力モードにする。
    ADCON0 = 0b00000001 | (PORT_NO_ADC << 2); //AD変換を有効化。
    ADCON1 = 0b01000000; // 0:(ADFM=0)精度10bit, 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF
    CM1CON0 = 0x00; //コンパレータを無効化。
}

uint16_t getAdc(void)
// A/D変換した値を得る。
// 戻り値: A/D 変換されたアナログ信号の大きさ(0?1023)
{
    uint16_t adcvalue;
    //ADC
    wait(10);
    GO_nDONE = 1;
    while (GO_nDONE);
    adcvalue = ADRESH << 2 | ADRESL >> 6;
    return adcvalue;
}

int8_t isdark(void)
// 光センサの出力から、「暗い」かどうか判断する。
// 戻り値; 「暗い」と判断したとき -1
//          それ以外 0
{
    uint16_t photo; // 光センサの出力値
    int8_t result; // 戻り値
    uint16_t darkness_threshold = 900; //「暗い」と判断する閾値
    photo = getAdc(); // 光センサの出力をA/D変換
    if (photo > darkness_threshold) { //「暗い」とき
        result = -1;
    } else { // 「明るい」とき
        result = 0;
    }
    return result;
}

// PWM (CCP1) 制御関数 (ブザー用PWM)

void initPwm(void) {
    //Timer2 settings for PWM standard mode
    PR2 = 0; // PWM Period = [(PR2) +1 ]*4*(1/FOSC)*(Timer2 Prescale value)
    T2CON = 0b00000010; //00 = Prescaler is 1, 01 = Prescaler is 4,
    // 10 = Prescaler is 16,  11 = Prescaler is 64

    APFCON &= ~(1 << CCP1SEL); //CCP1 function is on RA2
    //CCP1SEL = 1;//CCP1 function is on RA5
    CCP1CON = 0b00001100; //PWM mode: P1A, P1C active-high; P1B, P1D active-high
    CCPR1L = 64; // PWM Duty Cycle (CCPR1L:CCP1CON<5:4>)*(1/FOSC)*(TMR2 Prescale Value)
    //Start PWM
    TMR2 = 0;
    TMR2ON = 1;
}

void stopPwm(void) {
    CCP1ASE = 1;
    TMR2ON = 0;
}

void startPwm(void) {
    CCP1ASE = 0;
    TMR2ON = 1;
}

void pwmPulseWidth(uint8_t value) {
    CCPR1L = value;
}

void pwmPulseFreq(uint8_t value) {
    PR2 = value;
}

// LED用PWM関連
volatile int32_t PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWM Control Parameters
volatile int32_t dpwm = 1; // LEDの明るさをPWM制御するためのカウンタの増分

void pwmledoff(void) {
    PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWMの設定(消灯)
    TMR1ON = 0; // Timer1 Interrupt Disable
    TMR1IE = 0; // Timer1 Interrupt Disable
    led0off(); //LED0 off
}

void pwmledon(int32_t period) {
    PWMCounter = 0;
    PWMPeriod = period;
    PWMCounterOn2Off = 1; // PWMの設定
    TMR1ON = 1; // Timer1 Interrupt Enable
    TMR1IE = 1; // Timer1 Interrupt Enable
}

void setpwmledperiod(int32_t period) {
    PWMPeriod = period; // PWM周期の設定
}

void initTimer1(void) {
    // Initialize TIMER1 to control PWM-LED Flash
    TMR1CS1 = 0;
    TMR1CS0 = 0; // Internal clock (Fosc/4)
    //    T1CKPS1 = 1; T1CKPS0 = 1;// prescaler 1:8
    T1CKPS1 = 0;
    T1CKPS0 = 0; // prescaler 1:1
    TMR1H = 0xff;
    TMR1L = 0x00; // Clear Timer1 counter
    TMR1ON = 1; // Enable Timer1
    TMR1IE = 1; // Timer1 Intterupt Enable
    PEIE = 1;
    GIE = 1;
}

void intTimer1(void) {
    // Timer1割込みのとき実行される関数
    // LED0をじわっと点灯
    TMR1H = 0xff;
    TMR1L = 0x00; // Clear Timer1 counter

    if (PWMPeriod > 0) {
        if (PWMCounter < PWMCounterOn2Off) {
            led0on();
        } else {
            led0off();
        }
        PWMCounter++;
        if (PWMCounter >= PWMPeriod) {
            // PWMの1周期が完了したら、
            PWMCounter = 0;
            PWMCounterOn2Off += dpwm;
            if (PWMCounterOn2Off >= PWMPeriod) {
                dpwm = -1; // 徐々に暗くするPWMに変更
            } else if (PWMCounterOn2Off <= 0) {
                dpwm = 1; // 徐々に明るくするPWMに変更
            }
        }

    } else {
        led0off();
    }
}

// Timer0 制御関数
volatile uint32_t TimerCounter; // Counter of Timer Interrupt
#define T0UNIT 15UL // TimerCounter at 1 sec.

void initTimer0(void)
//Initialize Timer 0
// count up every 256*256us = 65536us = 65.536ms (clock 4/4=1MHz, prescaler 1:256
// if you want t[s] timer, TimerCount <= t / 0.065536
{
    TMR0IF = 0; // interrupt flag clear
    TimerCounter = 0; // Timer Interrupt Counter clear
    OPTION_REG &= 0b11000000; // Masking Timer0 bits
    OPTION_REG |= 0b00010111; // Timer0 Internal clock, Prescaler 1:256
    // Timer0 Interrupt occured every 65.536ms
    TMR0IE = 1; // enable Timer0 interrupt
    GIE = 1; // enable interrupt
}

void waittimeout0(uint32_t timeLimit) {
    uint32_t timeLimitCount = timeLimit * T0UNIT; // 時間切れまでのカウント数
    TimerCounter = 0; // タイマのリセット。計時開始。
    timeLimitCount = T0UNIT * timeLimit;
    while (TimerCounter < timeLimitCount) {
        //制限時間になるまでの処理
    }
}

void starttimer0() {
    TimerCounter = 0; // タイマのリセット。計時開始。
}

int8_t istimeout0(uint32_t timeLimit)
//指定時間に達していなければ、0xffを返し、指定時間に達していたら0x0を返す。
{
    uint8_t retvalue;
    if (TimerCounter < T0UNIT * timeLimit) {
        retvalue = 0xff;
    } else {
        retvalue = 0x00;
    }
    return retvalue;
}

//割込処理

void initExtInt(void) {
    // 外部割込み初期化
    IOCAN3 = 1; //Interrupt-On-Change port-A Negative edge (RA3)
    IOCIE = 1; //Interrupt-On-Change Interrupt Enable
    GIE = 1; //Global Interrput Enable
}

void stopextint(void) {
    IOCAN3 = 0; //Interrupt-On-Change port-A Negative edge (RA3) disable
}

void startextint(void) {
    IOCAN3 = 1; //Interrupt-On-Change port-A Negative edge (RA3) enable
}

void buz(void) {
    pwmPulseFreq(0x50);
    pwmPulseWidth(0x20);
    startPwm();
    wait(2000);
    stopPwm();
}

void startbuz(uint8_t freq) {
    pwmPulseFreq(freq);
    pwmPulseWidth(0x20);
    startPwm();
}

void stopbuz(void) {
    stopPwm();
}

void initialize(void) {
    initIo(); // I/O port初期化
    initPwm(); // PWM (CCP1) 初期化
    initTimer0(); // Timer0 初期化
    initTimer1(); // Timer1 初期化
    pwmledoff(); //LEDじわっと点灯の停止
    while (isswon()); //タクトスイッチがOFFするまで待つ
    initExtInt(); // 外部割込み初期化
    initAdc(); // A-D convertor 初期化
    stopPwm(); // PWM (CCP1) 無効化

}

////// ここまで、プログラミングのためのデータや関数を定義する /////

// 割込み処理関数

void intRA3(void) {
    // RA3変化による外部割込み発生時に処理される
    buz(); // ブザを鳴らす
}

void __interrupt() intr(void)
// Interrupt processing function
{
    if (INTF == 1) {
        INTF = 0;
    }
    if (TMR0IF == 1) {
        TMR0IF = 0; //Clear Timer0 Interrupt flag
        TimerCounter++;
    }
    if (TMR1IF) {
        TMR1IF = 0; // Clear Timer1 Interrupt Flag
        intTimer1(); // Timer1割込みの処理関数を実行
    }
    if (IOCAF3 == 1) {
        // cleae external interrupt flag
        IOCAF3 = 0;
        if (isswon()) {
            // タクトスイッチがON(LOW)なら割込み処理
            // IOCAN3 = 1(立下がりエッジ検出)のとき、立上りエッジを検出してしまうための対策
            intRA3(); // RA3変化割込みの処理関数を実行
        }
    }
}
// 割込み処理関数ここまで

////// この後のmain(void) { から }までの間に、プログラムが書ける。 /////

void main(void) {

    initialize(); // マイコン初期化

    pwmledoff(); // LED PWM点灯を停止
    led0off();
    led1off();
    led2off(); //LED0,1,2を消す

    startbuz(189); //E(ミ)の音
    wait(5000);
    startbuz(212); //D(レ)の音
    wait(5000);
    startbuz(232); //C(ド)の音
    wait(5000);
    stopbuz(); //音を止める

    while (1) { //無限ループ。次の}までの間を無限回繰り返す。
        if (isdark()) {
            // 暗いとき
            pwmledon(50); //LED0をじわっと光らせる
            starttimer0();
            while (istimeout0(10)) {
                //10秒間実行
                if (!isdark()) break; //明るくなったら抜ける
            }
            pwmledoff(); //LED0じわっと点灯終了
            while (isdark()); // 暗い間待つ
        } else {
            // 明るいとき
            if (isswon()) {
                pwmledoff(); //LED0のじわっと点灯を停止
                //スイッチONになったら
                led0on();
                led1on();
                led2on(); //LED0,1,2点灯
                starttimer0(); // タイマ0計時開始
                waittimeout0(5); //5秒待つ
                led0on();
                led1on();
                led2off(); // LED0,1点灯, LED2消灯
                buz(); //ブザ鳴らす

                starttimer0(); // タイマ0計時開始
                while (istimeout0(5)) {
                    //5秒待つ
                    if (isdark()) break; //暗くなったらおしまい
                }
                led0on();
                led1off();
                led2off(); // LED0点灯, LED1,D2消灯

                buz(); //ブザ鳴らす

                starttimer0(); // タイマ0計時開始
                while (istimeout0(5)) {
                    //5秒待つ
                    if (isdark()) break; //暗くなったらおしまい
                }
                led0off();
                led1off();
                led2off(); // LED0,1,D2消灯

                for (int8_t i = 0; i < 5; i++) {
                    //5回繰り返す
                    startbuz(50); //終了音
                    wait(20000);
                    stopbuz();
                    wait(10000);
                }
            }
        }

    }
}

////// この上の } より上にプログラムが書ける。 /////