●スイッチを取り付ける

PICマイコンのポートにスイッチを取り付けてLEDをON/OFFできるようにしたいと思います。
まずは、
LEDを点滅させるで使った基盤を改造して空いているポートに押しボタンスイッチを取り付けました。
スイッチ自体のプルアップはPICの弱プルアップ機能を使います。
PICはとりあえず、PIC10F222を使用したいと思います。



▼LEDを全点灯させるプログラム

まずは、LEDが1で点灯するのか0で点灯するのか確認の為に全点灯するプログラムを書いてみます。
#include <stdio.h>
#include <stdlib.h>

#pragma config IOSCFS = 8MHZ    // Internal Oscillator Frequency Select bit (8 MHz)
#pragma config MCPU = OFF        // Master Clear Pull-up Enable bit (Pull-up enabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config CP = OFF         // Code protection bit (Code protection off)
#pragma config MCLRE = OFF      // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)

#include <xc.h>
#define _XTAL_FREQ 8000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    OSCCAL = 0b00000000;
    OPTION = 0B10000000;  //GP0 GP1 GP3の弱プルアップ有効
    ADCON0 = 0B00001100;
    TRISGPIO = 0b00001000;
    GPIO = 0b00000111;
    return (EXIT_SUCCESS);
}

このプログラムを動作させると、このように全点灯になりました。

赤文字部分でGPIOに2進数の1を書き込み、LEDを点灯させています。
次に、LEDを全部消すプログラムを書いてみます。
int main(int argc, char** argv) {
    OSCCAL = 0b00000000;
    OPTION = 0B10000000;
    ADCON0 = 0B00001100;
    TRISGPIO = 0b00001000;
    GPIO = 0b00000000;
    return (EXIT_SUCCESS);
}
赤文字部分を、GPIO = 0b00000000; に変更し実行させてみました。

なにも点灯しません。
これはこれで、正常な動作だと確認できます。


▼ボタンを押している間だけ点灯するプログラム

OPTIONレジスタでのGP0 GP1 GP3の弱プルアップ有効のため、スイッチの取り付けてあるポートにプルアップ抵抗は必要ありません。
もし、使用するPICにその機能が無いのなら、プルアップ抵抗を取り付ける必要があります。

int main(int argc, char** argv) {
    OSCCAL = 0b00000000;
    OPTION = 0B10000000;
    ADCON0 = 0B00001100;
    TRISGPIO = 0b00001000;
    while (1) {
        if (GP3 == 1) {
            GPIO = 0b00000000;
        } else {
            GPIO = 0b00000111;
        }
    }
    return (EXIT_SUCCESS);
}
このプログラムだとボタンを押している間だけLEDが点灯します。


▼ボタンを押したらON、もう一度押したらOFFにする

ボタンを一回押したらON/OFFするにはチャタリング対策が必要になってきます。
チャタリングとは、スイッチをON/OFFした瞬間、接点が振動し短時間ON/OFFを繰り返す事です。
マイコンは速いですから短時間のON/OFFも拾ってしまい、意図しない動作をします。

#include <stdio.h>
#include <stdlib.h>

#pragma config IOSCFS = 8MHZ
#pragma config MCPU = OFF   
#pragma config WDTE = OFF   
#pragma config CP = OFF     
#pragma config MCLRE = OFF  

#include <xc.h>
#define _XTAL_FREQ 8000000 //クロック周波数 __delay_ms()に必要
#define NUM_OF_READS 64  //読みだし回数

unsigned int chatteringCut_pullupPin(uint8_t*port, uint8_t mask, uint8_t*sw) {
    unsigned int _on = 0, _off = 0;
    while (1) {
        if (*port & mask) {
            //OFFの場合
            _off++;
            _on = 0;
        } else {
            //ONの場合
            _off = 0;
            _on++;
        }
        if (_on > NUM_OF_READS || _off > NUM_OF_READS) break;
        //__delay_ms(1);
    }
    if (_on > NUM_OF_READS) {
        //ONで終了した場合 ONが押されっぱなしでも戻り値が1を返し続けないようにしている
        if (*sw) {
            return 0;
        } else {
            *sw = 1;
            return 1;
        }
    } else {
        //OFFで終了した場合
        *sw = 0;
        return 0;
    }
}

int main(int argc, char** argv) {
    OSCCAL = 0b00000000;
    OPTION = 0B10000000;
    ADCON0 = 0B00001100;
    TRISGPIO = 0b00001000;
    int i = 0;
    char sw1 = 0;
    while (1) {
        if (chatteringCut_pullupPin(&GPIO, 0b00001000, &sw1)) {
            i = !i;
        }
        if (i) {
            GPIO = 0b00000111;
        } else {
            GPIO = 0b00000000;
        }
    }
    return (EXIT_SUCCESS);
}
このプログラムだと、ボタンを一度押したらON、もう一度押したらOFFになります。
簡単な事なのですが複雑なプログラムになってしまいました。
しかも、これだけでメモリ18バイト使用しています。
賛否が別れる所ですが、全メモリが23バイトのマイコンではキツイですね。


■ボタンを押している間だけ点灯するプログラムを他の種類のPICでも動作させる

一から作るのは面倒なのでLEDを点滅させるで使った回路を改造して使用します。

赤い線の部分を改造しました、スイッチを取り付けている線にはLEDと抵抗がマイナス側に付いているためプルダウンしているのと同じになります。
スイッチを押すと、PICのRA0とB0にはプラスの電圧がかかり、スイッチを離すとマイナスの電圧がかかる事になります。


▼PIC16F84でのプログラム

#include <stdio.h>
#include <stdlib.h>

#pragma config FOSC = HS 
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config CP = OFF  

#include <xc.h>
#define _XTAL_FREQ 10000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    TRISA = 0b00000001; //portAを1:入力、0:出力
    TRISB = 0b00000001; //portBを1:入力、0:出力

    while (1) { // 無限ループ
        if (RA0 || RB0) {
            PORTA = 0b00000000; //portAに出力
            PORTB = 0b00000000;
        } else {
            PORTA = 0b11111111;
            PORTB = 0b11111111;
        }
    }
    return (EXIT_SUCCESS);
}
PIC16F84ではTRISを操作して入力と出力のビットを切り替えています。
古い初期の頃のPICですから構造がシンプルでいいですね。


▼PIC16F648Aでのプログラム

〇 RB0をデジタル入力にするプログラム
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = HS 
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF 
#pragma config CPD = OFF 
#pragma config CP = OFF  

#include <xc.h>
#define _XTAL_FREQ 10000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    TRISA = 0b00000000; //portAを1:入力、0:出力 RA0を入力に設定
    TRISB = 0b00000001; //portBを1:入力、0:出力 全てを出力に設定

    while (1) { // 無限ループ
        if(RB0){
            PORTA = 0b00000000; //portAに出力
            PORTB = 0b00000000;
        }else{
            PORTA = 0b11111111;
            PORTB = 0b11111111;
        }
    }
    return (EXIT_SUCCESS);
}
RBは他の機能が割り当てられていないためPIC16F84のようにシンプルです。
RA0は最初はアナログ入力になっていますので切り替える必要が出てきます。

〇 RA0をデジタル入力にするプログラム
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = HS 
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF 
#pragma config CPD = OFF 
#pragma config CP = OFF  

#include <xc.h>
#define _XTAL_FREQ 10000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    TRISA = 0b00000001; //portAを1:入力、0:出力 RA0を入力に設定
    TRISB = 0b00000000; //portBを1:入力、0:出力 全てを出力に設定
    CMCON = 0b00000111; //コンパレータをOFFにする
    VRCON = 0b00000000; //リファレンス電圧をOFFにする
    while (1) { // 無限ループ
        if (RA0) {
            PORTA = 0b00000000; //portAに出力
            PORTB = 0b00000000;
        } else {
            PORTA = 0b11111111;
            PORTB = 0b11111111;
        }
    }
    return (EXIT_SUCCESS);
}
赤文字部分でRAの入力をデジタルに切り替えています。


▼PIC16F1827でのプログラム

このPICではRA、RB両方とも、最初はアナログ入力になっていますのでデジタル入力に切り替える必要があります。
#include <stdio.h>
#include <stdlib.h>
// CONFIG1
#pragma config FOSC = HS     
#pragma config WDTE = OFF    
#pragma config PWRTE = ON    
#pragma config MCLRE = ON    
#pragma config CP = OFF      
#pragma config CPD = OFF     
#pragma config BOREN = ON    
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF    
#pragma config FCMEN = OFF   
// CONFIG2
#pragma config WRT = OFF     
#pragma config PLLEN = OFF   
#pragma config STVREN = ON   
#pragma config BORV = HI     
#pragma config LVP = OFF     

#include <xc.h>
#define _XTAL_FREQ 10000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    TRISA = 0b00000001; //portAを1:入力、0:出力 全てを出力に設定
    TRISB = 0b00000001; //portBを1:入力、0:出力 全てを出力に設定
    ANSELA = 0b00000000; //ポートAをデジタル入力にする
    ANSELB = 0b00000000; // ポートBをデジタル入力にする
    while (1) { // 無限ループ
        if (RA0 || RB0) {
            PORTA = 0b00000000; //portAに出力
            PORTB = 0b00000000;
        } else {
            PORTA = 0b11111111;
            PORTB = 0b11111111;
        }
    }
    return (EXIT_SUCCESS);
}
赤文字部分で入力をデジタルに切り替えています。


▼PIC12F1822でのプログラム

8ピンPICのため、LEDを点滅させるで使用した回路を改造して使用します。
改造は上で使用した18ピンのPICと同じで通常はプルダウン、ボタンを押すとプルアップです。

このPICは比較的新しいらしくてF1シリーズと呼ぶらしいです。
上の同じF1シリーズのPIC16F1827と同じ操作で、入力をデジタルに切り替えます。
#include <stdio.h>
#include <stdlib.h>
// CONFIG1
#pragma config FOSC = INTOSC 
#pragma config WDTE = OFF    
#pragma config PWRTE = ON    
#pragma config MCLRE = OFF   
#pragma config CP = OFF      
#pragma config CPD = OFF     
#pragma config BOREN = ON    
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF    
#pragma config FCMEN = OFF   
// CONFIG2
#pragma config WRT = OFF     
#pragma config PLLEN = OFF   
#pragma config STVREN = ON   
#pragma config BORV = HI     
#pragma config LVP = OFF     

#include <xc.h>
#define _XTAL_FREQ 16000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    OSCCON = 0b01111010;
    ANSELA = 0b00000000;
    PORTA = 0b00000000;
    TRISA = 0b00000001; //portAを1:入力、0:出力 全てを出力に設定

    while (1) { // 無限ループ
        if (RA0) {
            PORTA = 0b00000000; //portAに出力
        } else {
            PORTA = 0b00110111;
        }
    }
    return (EXIT_SUCCESS);
}
スイッチをON/OFFするとLEDが点灯するのが確認できます。

▲トップページ > マイコンなど