今回はスイッチ押すと点滅を開始するようにプログラムを作成して動作確認してみます。
本シリーズ記事の内容を改訂して、基礎編、応用編、実践編として以下のリンクに公開しています。以下のシリーズはさらにいろいろなPICマイコンの機能をご紹介しています!
PICマイコン電子工作入門 〜基礎編〜
PICマイコン電子工作入門 〜応用編〜
PICマイコン電子工作入門 〜実践編〜
今回の説明
発光ダイオード点滅回路を完成させるために以下の順序で説明しています。このエントリの説明は(5)「ベース回路にスイッチを追加」の部分になります。
- 発光ダイオード(LED)を電池と抵抗のみで光らせる回路を組む
PICマイコンの回路を組む前に、まずは電池、抵抗、発光ダイオードのみを使って、ブレッドポード上に回路を組んで発光ダイオードを光らせてみます。ここでは電池、抵抗、発光ダイオードの回路記号、回路図と、回路図からブレッドボードに組む方法を説明します。 - PICマイコンのベース回路を組む
はじめの一歩の回路は、発光ダイオードを1秒に1回光らせるだけの回路です。この回路を組みます。 - プログラムを作る
発光ダイオードを1秒に1回光らせるプログラムを作成します。 - PICマイコンに書き込んで動作させる
作成したプログラムをPICマイコンに書き込んで動作させてみます。 - ベース回路にスイッチを追加
発光ダイオードの点滅をスイッチで開始させるために、ベース回路にスイッチを追加します。これまでは発光ダイオードを光らせる、という出力制御をしましたが、PICマイコンで外部から信号を入力する方法を確認します。 - ベース回路にブザーを追加
スイッチ付きの1秒に1回光らせる回路を作りましたので、ブザーを追加してタイマーを作ってみます。
ベースプログラム変更概要
発光ダイオードの点滅制御プログラムの構成をもう一度確認してみます。
このような構成でしたよね。これからスイッチの処理を追加しますが、何をどう直して何をどこに追加すればいいのでしょうか。
まずはじめのコメント部分ですが、これからベースのプログラムを編集していきますので、どのような機能を入れたかメモしておくことにします。
次にヘッダファイルインクルード部分ですが、スイッチの状態検知などPIC12F683の基本的な制御については xc.h のインクルードだけで問題ありませんので特に変更は必要ありません。
PICマイコンコンフィグレーション設定ですが、こちらも特にPICマイコンの振る舞いを変えませんので、特に変更は必要ありません。例えば、内部クロックから外部クロックに変更するとか、MCLRピン(4番ピン)に外部リセットスイッチを付けるなどという場合はこのコンフィグレーション設定をその変更に合わせて編集する必要があります。
その他設定部分についても特に追加はありません。
ということで、スイッチ制御するにはほぼメイン関数内のプログラム変更となります。それでは次にメイン関数をどのように修正すればよいかざっと把握しておきましょう。
内部クロックは特に変更しませんのでこのままです。
ピンの設定ですが、ベースのプログラムはすべてのピンを「デジタル」で「出力」に設定しました(MCLRピン/4番ピンは入力固定です)。今回はGP4ピン(3番ピン)を「入力」にしたいので、この部分は変更が必要になりますよね。具体的には「GP4ピンを入力」に変更します。
残りの「ピンの初期設定」と「動作処理」はいろいろと変える必要がありそうですよね。この動作処理の変更については以下、順を追って詳しく説明します。
ということで、このあと、以下の内容を詳しく説明します。
- 動作仕様の決定
- ピンの設定部分変更
- ピンの初期設定部分変更
- 動作処理部分変更
動作仕様の決定
ベース回路は電源を供給するとすぐに点滅を開始させていました。今回はスイッチをつけて、スイッチが押されたら点滅を開始する、というように変更します。動作仕様は何通りか考えられますが、点滅を開始するまえは、発光ダイオードは点灯しておきたいと思います。電源を供給しても、発光ダイオードが消灯したままですと、動いているのかよくわからないですよね。ということで点滅を開始するまでは「ちゃんと電源がきていて、プログラムも動いていますよ」ということを示すために、発光ダイオードの初期状態は点灯状態にしておこうと思います。その状態でスイッチが押されたら点滅を開始する、という動作仕様にします。
これを図にまとめると以下のようになります。
ピンの設定部分変更
それではメイン関数を編集していきましょう。まずピンの設定部分ですが、ベースプログラムでは、内部クロックの設定(OSCCON)、アナログ/デジタル設定(ANSEL)、入出力設定(TRISIO)の設定をしています。内部クロックはそのまま1MHz、アナログ/デジタル設定もすでに全ピンデジタル設定にしてありますので変更なしです。今回、GP4ピン(3番ピン)を入力に変更します。TRISIOは現在以下のように設定しています。
出力ピンにしたいときはそのピンのビットを0、入力ピンにしたいときは1にすればよいので、GP4のビットを1にします。具体的には以下のような設定になります。
ビット表現では、0b00010000になりますので、16進数では0x10となります。ということで、TRISIOは
TRISIO = 0x00;
から
TRISIO = 0x10;
に変更すればOKです。これでGP4ピン(3番ピン)は入力ピンに設定できました。
ピンの初期設定部分変更
次にピンの初期設定部分です。今回はスイッチが押されるまで、発光ダイオードを点灯しておく動作仕様としました。現在は電源投入直後は発光ダイオードを消灯しておくようにしています(GP5=0;)。これを点灯するように変更します。現在の
GP5 = 0;
から
GP5 = 1;
に変更すればOKです。
動作処理部分変更
このままですと、次の点滅処理が始まってしまいます。スイッチが押されてから点滅処理を開始するように変更すればよいので、今の点滅処理の前に、スイッチが押されるまで、何もしないでずっと待ち続けれる処理を追加すればいいですよね。
まず、スイッチの状態はGP4という変数(レジスタ)に格納されます。GP4ピンが0Vのときは0、5V(電源電圧)のときは1がリアルタイムに格納されます。スイッチが押されるまで何もしない、ということは、変数GP4が0の間は次に進まないでずっと待ち続ける、というプログラムを書けばよいことになります。これは以下のようなプログラムを追加すればOKです。
while(GP4 == 0) {
}
またスイッチが押されたら点滅を開始しますが、発光ダイオードは消灯の状態から始まります。そのため、スイッチが押された後は発光ダイオードを消灯する必要があります。そのため、上のwhile文の後にGP = 0;を追加します。
完成したプログラム
これで一通り変更できましたので、コメントに現在のプログラム内容をメモして完成です。以下は完成したプログラムです。コメントを入れましたので、処理の流れをよく確認してみてくだい。
理解したらこのプログラムをビルドして書き込み、動作確認してみましょう。一発で動かないこともあるかもしれません。その場合は諦めずに、ひとつひとつ問題がないか確認していきます。どうしてもわからない場合はコメント欄かCONTACTページからご連絡いただければ、一緒に考えてみたいと思います。
/*
* File: main.c
* Author: Tool Labs
* プログラム内容:
* 電源投入後はスイッチ(GP4)が押されるまで
* 発光ダイオード(GP5)を点灯、スイッチが押されたら
* 点滅を開始する。
*/
// インクルードファイル
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// PIC12F683コンフィグレーションビット設定
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#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 CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
/*
* main()関数
*/
int main(int argc, char** argv) {
// PICマイコン初期化
OSCCON = 0x40; //クロック周波数を1MHzに設定
ANSEL = 0x00; //すべてのピンをデジタルモードに設定
TRISIO = 0x10; //GP4を入力ピン、それ以外を出力ピンに設定。ただしGP3ピンはもともと入力固定
// LEDのピン(GP5)を1にして、LEDを点灯させる
GP5 = 1;
// スイッチ(GP4)がOFFの間、何もしないで待ち続ける
while(GP4 == 0){
}
// スイッチが押されたら、上のwhileループを抜けるので、LEDの点滅を開始する
// まずはじめにLEDのピン(GP5)を0にして、LEDを消灯する
GP5 = 0;
// 点滅を永遠繰り返す
while(1){
// まず、消灯状態のまま950ミリ秒待つ
__delay_ms(950);
// 950ミリ秒経過したら、LEDのピンを1にして、LEDを点灯する
GP5 = 1;
// LED点灯状態で50ミリ秒待つ
__delay_ms(50);
// 50ミリ秒経ったら、LEDのピンを0にして、LEDを消灯する
GP5 = 0;
}
// ここには到達しない
return (EXIT_SUCCESS);
}
このあとの説明
スイッチの処理、思ったより簡単でしたよね。ねっ。スイッチに接続したピンに対応する変数(この場合はGP4)には現在のスイッチ状態に応じた値が入っていますので、それをみて必要な処理をすればいいだけです。
と、大半の方が感じると思うのですが、実は、スイッチには根深い問題があります。この根深い問題については後で詳しく説明します。おそらくげんなりすると思います。でもスイッチのことを嫌いにならないでくださいね。
更新履歴
日付 | 内容 |
---|---|
2015.9.22 | 新規投稿 |
2018.12.3 | 新シリーズ記事紹介追加 |
お世話になります
例文を弄って点滅の個所にこのようなコードを入れて
点滅のON/OFFを切り替えたりしたのですが、4,5回に一回位しか反応しません
原因は何なのでしょうか?
何か他に方法有れば教えてください
ご質問どうもありがとうございます。
「点滅の切り替え」とのことですが、具体的にどのようなLED動作になりますでしょうか。書かれたコードを日本語で書くと以下のようになります。
また、このコードをwhile文の中に書かれていると思いますが、
上のコードは1秒に1回処理されますので、if文判断のタイミングでスイッチを押していないと処理されないことになります。
サンプルのプログラムではwhile文は950ms待ちと50ms待ちがありますので、LEDの点滅中のボタン制御には適していません。
LEDの点滅中にボタン制御したいということでしたら、全く異なるプログラムになります。
きちんとしたお答えができずに申し訳ございません。
返信ありがとうございます
待ち関数が動いている時に作動しないので
950m秒以上押し続けてOFFの機能が限界の様な気がしました
ONは50m秒待ちなので押し続けなくても失敗しないです
半導体の方は殆ど意味が分からないんですが(特に電流の流れなど複雑で)
プログラムで何となく理解した様な気持ちになれますね
while(1){
if(GP4==1&&c==0){c=1;__delay_ms(1000);}
if(GP4==1&&c==1){c=0;__delay_ms(1000);}
if(c==0){
// まず、消灯状態のまま950ミリ秒待つ
__delay_ms(950);
// 950ミリ秒経過したら、LEDのピンを1にして、LEDを点灯する
GP5 = 1;
}
追記で、
GP4のスイッチのON/OFFの制御は電流が流れたかどうかの有無で
GP4ピンに戻って判定しているという理解で良いんでしょうか?
ご質問どうもありがとうございます。
PICマイコンに限らず、Arduino、Raspberry PiなどGPIOを持つものはすべて電圧で判別しています。電流で判別するということはないです。
いずれこのサイトで電子回路入門のシリーズ記事を書こうと思っております。今回のご質問いただいた内容も参考になります。解説方法を検討しようと思います。
数値の電流じゃなくて電子の流れです
電子の流れが視覚化できればそこまで難しい事を
してないと思うんですよね
GPIOの状態の読み取りは、電子の流れはありません。ちょっとわかりづらいかもしれません。
普通の乾電池が単独で存在しているとき、電池の両端の電圧は1.5Vになります。このとき電子の流れはありません。電子の流れはなく、電圧だけが存在することになります。
GPIOの読み取りはこの電圧の読み取りを行なっています。電子の動きはない、ということになります。(厳密には漏れ電流がありますので電子の動きはほんの少しありますが、その電子の流れは電圧とは関係ない、ということになります)
情報どうもありがとうございました。
LEDの点滅を停止する、ということですね。
現在のプログラムは950ms待ちをしているので、
時間の正確性は低くなりますが、950ms待つのではなく、
50msを19回繰り返し、繰り返しごとにスイッチ状態を確認する、
という方法があります。
例えばこのような感じで停止できると思います。
待機時間を更に細分化とは思いつきませんでした
これならどのタイミングでもON/OFF出来そうです
950msを50msをfor文で回しますので、for文、for文内のif文の処理の時間分ズレが出てしまいます。時計として使用することはできませんが、短い時間の制御でしたら問題ないと思います。