目次

PIC(12F1822)でNEC照明リモコンを作る

製作のきっかけ

現在の住居では玄関用の照明が設置されておらず、台所用の照明を利用します。

また台所照明の壁スイッチは玄関付近になく、夜間時の外出時はリモコンを玄関付近まで

移動させます。

しかしながら、日中は照明を点灯させる必要がないので、夜間に帰宅した場合など、

リモコンの移動させて置くことを忘れる事が多い。

つまりは、リモコンがもう1台ほしい。

リモコン制作にあたり

以下の条件で制作

・ 玄関用なので、OFF/ON ボタンだけあれば良い

・ 出来る限り安価 (自宅にある部品のみで)

  リモコン型番は NEC RE0208 Amazonで代替リモコン 約800円で購入可能
  配送料の兼ね合いもあるが、買ったほうが安い場合を考慮する

・ PWMを使うと製作が簡単らしいので、自宅在庫の12F1822を利用。

  今回の要件を考えると12F822はオーバースペックなのだが、PWM機能がある12F1822より安価な在庫は、自宅に無い。

回路図

製作後に実際に玄関付近だと、赤外線出力が弱いのか未反応

ベース抵抗を1KΩに変更して、何とか(リモコンを照明に向けると)使える状態となった。

純正リモコンでは単3電池2本での3V。赤外線LEDは3つあるので、リモコンの向きは照明に向けなくても反応する

製作したリモコンに不満を感じたら改善したい。

プログラム

main.c

/*
 * File:   main.c
 * Author: kampari
 * PIC:    12F1822
 * MPLAB X IDE V6.20
 * XC8 V2.46
 * 
 *            VDD 1     8 Vss
 *      -   (RA5) 2     7 (RA0)   -    
 *      -   (RA4) 3     6 (RA1)   -
 *  SW [IN] (RA3) 4     5 (RA2) [OUT] PWM
 * 
 * Created on 2024/10/11, 20:30 JST
 */

#include <xc.h>

#define _XTAL_FREQ 4000000
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = HI        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
#pragma config DEBUG = OFF      // In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins
 
// NEC シーリングライト CH2 ON/OFF
// コマンド 	コード (CH1) 	コード (CH2)
// ON/OFF 	82 6d ab 54 	82 6d 2b d4
// 参考URL      https://shrkn65.nobody.jp/remocon/database.html
const unsigned char IR_FixData[4] = { 0x82, 0x6d, 0xab, 0x54 };

void Regster_Int(void) {
    // 基本機能の設定
    OSCCON      = 0b01101010;   // 内部オシレーター 4MHz
    TRISA       = 0b00001000;   // RA3: IN 他:すべてOUT
    ANSELA      = 0b00000000;   // アナログモードは使用しない
    OPTION_REGbits.nWPUEN = 0;  // 弱プルアップ個別設定を有効にする
    WPUA        = 0b00001000;   // RA3のみ弱プルアップをセットする
    PORTA       = 0b00001000;   // RA3: Hi 他:すべてLow
    
    //PWMモード設定
    APFCONbits.CCP1SEL = 0;     // RA2をCCP1(PWM)のP1A使用
    CCP1CONbits.P1M = 0b00;     // PWM モード, シングル出力
    CCP1CONbits.CCP1M = 0b1100; // PWM モード, アクティブ High
    
    // TMR2設定
    T2CONbits.T2OUTPS = 0b0000; // ポストスケーラを1:1に設定
    T2CONbits.T2CKPS  = 0b00;   // プリスケーラを1:1に設定
    T2CONbits.TMR2ON  = 0;      // TMR2ON OFF
                                // T2CON = 0b00000000; の設定と同義
                                // 電源ON時の初期値は0だが、備忘
    TMR2 = 0;                   // Timer2カウンタを0
    
    /*
     * 参考URL: http://eng-serve.com/pic/pic_pwm.html
     * 16F877A用の計算サイト (12F1822だけど、たぶん同じだろう・・・)
     * PWM Register Values
     * Oscillator Frequency Fosc = 4000000
     * Clock Frequency Fclk = 1000000
     * PWM Freq = 38000 desired...actual: 38462
     * Prescaler Value = 1
     * PR2 = 25
     * Maximum duty value = 105 
     * Requested Duty Value = 50
     * 
     * Code Provided AS IS....Use at your own risk!
    */

    // be sure to set PWM port as output
    // T2CON = 0b00000100; // prescaler + turn on TMR2;
    // PR2 = 0b00011001;
    // CCPR1L = 0b00001101;  // set duty MSB
    // CCP1CON = 0b00011100; // duty lowest bits + PWM mode

    PR2    = 0b00011001;
    CCPR1L = 0b00001101; 
    CCP1CONbits.DC1B = 0b01; 

    // 割込み設定
    INTCON           = 0b00000000;  // 割込み初期化 (無効)
    IOCANbits.IOCAN3 = 1;           // RA3がHi-> Low (立下り時) 割込み設定   
}

int main(int argc, char** argv) {

    unsigned char   codebyte;
    Regster_Int();                  // レジスター初期設定
    __delay_ms(50); 

    for (;;) {
        if (PORTAbits.RA3 == 0){
            __delay_ms(50);         // 安定化待ち
        
            //リーダーコード送信
            T2CONbits.TMR2ON = 1;
            __delay_us(9000);
            T2CONbits.TMR2ON = 0;
            __delay_us(4500);
         
            // カスタム/データコード送信
            for (int i=0;i<4;i++){
                codebyte = IR_FixData[i];
                for(int j=7;j>=0;j--){
                    T2CONbits.TMR2ON = 1;
                    __delay_us(560);
                    T2CONbits.TMR2ON = 0;
                
                    //PWM OFFの時間でOn/Off区別
                    if (codebyte & (0x80 >> j)) {
                        __delay_us(1690);       //Bit on
                    }
                    else {
                        __delay_us(560);        //Bit Off
                    }
                }
            }
            //ストップビット送信
            T2CONbits.TMR2ON = 1;
            __delay_us(560);
            T2CONbits.TMR2ON = 0;
            __delay_ms(42);         

            // リピート送信 (未点灯(送信エラー)防止策)
            T2CONbits.TMR2ON = 1;
            __delay_us(9000);
            T2CONbits.TMR2ON = 0;
            __delay_us(2250);

            // ストップビット
            T2CONbits.TMR2ON = 1;
            __delay_us(560);
            T2CONbits.TMR2ON = 0;
            __delay_ms(42);
        }
        
        //ボタンの押下があるまでスリープ  
        __delay_ms(1000);       // チャタリング対策
        // PORTAbits.RA5 = 0;   // テスト用LED を RA5に実装(動作確認用)
        IOCAFbits.IOCAF3 = 0;   // RA3の状態変化フラグを0にセット
        INTCONbits.IOCIE = 1;   // 状態変化割り込みを有効にする
        SLEEP();
        NOP();
        INTCONbits.IOCIE = 0;   // 状態変化割り込みを無効にする
        // PORTAbits.RA5 = 1;   // テスト用LED を RA5に実装(動作確認用)
    }
}