今回はスイッチを制御するプログラムを追加します。
今回の説明
回路を完成させるために以下の順序で説明しています。このエントリの説明は(5)「ベース回路にスイッチを追加する」の部分になります。
- LEDを電池と抵抗のみで光らせる回路を組み立てる
PICマイコンの回路を組み立てる前に、まずはブレッドボードに慣れておくことにします。電池、抵抗、LEDのみを使って、ブレッドポード上に回路を組んでLEDを光らせてみます。ここでは電池、抵抗、発光ダイオードの回路記号と回路図の説明をして、回路図からブレッドボードに組む方法を説明します。まずはブレッドボードに慣れましょう! - PICマイコンのベース回路を組む
はじめの一歩の回路は、LEDを1秒に1回光らせるだけの回路です。この回路をブレッドボードに組み立てます。 - プログラムを作る
LEDを1秒に1回光らせるプログラムを作成します。 - PICマイコンに書き込んで動作させる
作成したプログラムをPICマイコンに書き込んで動作させてみます。 - ベース回路にスイッチを追加する
LEDの点滅をスイッチで開始させるために、ベース回路にスイッチを追加します。これまではLEDを光らせる、という出力制御をしましたが、今度はPICマイコンで外部から信号を入力する方法を確認します。 - ベース回路にブザーを追加
スタートスイッチ付きの、1秒に1回光らせる回路を作りましたので、ブザーを追加してタイマーとして完成させます。
ベースプログラム変更概要
LEDの点滅制御プログラムの構成をもう一度確認してみます。
このような構成でしたよね。これからスイッチの処理を追加しますが、何をどう直して何をどこに追加すればいいのでしょうか。
最初のコメント部分ですが、これからベースのプログラムを編集していきますので、どのような機能を入れたかメモしておくことにします。
ヘッダファイルインクルード部分は、ピンの状態(スイッチの状態)検知などPIC12F1822の基本的な制御については新しいインクルードファイルは必要ありませんので、この部分は特に変更は必要ありません。
PICマイコンコンフィグレーション設定ですが、こちらも特にPICマイコンの振る舞いを変えませんので、特に変更は必要ありません。例えば、内部クロックから外部クロックに変更するなどの場合は、このコンフィグレーション設定をその変更に合わせて編集する必要があります。
その他設定部分についても特に追加はありません。
ということで、スイッチ制御するにはメイン関数内のプログラム変更となります。それでは次にメイン関数をどのように修正すればよいかざっと把握しておきましょう。
このプログラムは前回までに作成したものです。このプログラムに変更を加えていきます。
内部クロックは特に変更しませんのでこのままです。
ピンの設定ですが、ベースのプログラムは全てのピンを「デジタル」に設定しました。また入出力設定については、RA3を入力ピンに、それ以外を出力ピンに設定しました(RA3は入力しか設定できません)。スイッチはRA3ピンに接続しましたので、この設定も変更する必要はありません。
ということで「動作処理」の部分に、スイッチが押されたら点滅を開始する、という処理を追加する必要がありそうです。この動作処理の変更については以下、順を追って詳しく説明します。
- 動作仕様の決定
- 動作処理部分変更
動作仕様の決定
ベース回路は電源を供給するとすぐに点滅を開始させていました。今回はスイッチをつけて、スイッチが押されたら点滅を開始する、というように変更します。動作仕様は何通りか考えられますが、点滅を開始する前、つまりスイッチが押されるまで待機している時は、LEDを点灯しておきたいと思います。
電源を供給しても、LEDが消灯したままですと、動いているのかよくわからないですよね。ということで点滅を開始するまでは「ちゃんと電源がきていて、プログラムも動いていますよ」ということを示すために、LEDの初期状態は点灯状態にしておこうと思います。その状態でスイッチが押されたら点滅を開始する、という動作仕様にします。
これを図にまとめると以下のようになります。
この図で注意点があります。スイッチはプルアップ抵抗を接続しましたので、スイッチがOFFの場合はRA3ピンは5V、スイッチをONにすると0Vになります。プルアップ抵抗の場合はイメージと逆になりますので注意します。
動作処理部分変更
前回までに作成したプログラムでは、電源を入れるとすぐに点滅処理が始まってしまいます。スイッチが押されてから点滅処理を開始するように変更すればよいので、今の点滅処理の前に、スイッチが押されるまで、ひたすら待ち続ける処理を追加すればいいですよね。
スイッチの状態はRA3という変数(レジスタ)に格納されます。RA3ピンが0Vのときは変数のRA3が0に、5V(電源電圧)のときは1がリアルタイムに反映されます。
スイッチはプルアップ抵抗で5Vに接続されていますので、スイッチがOFFのとき、RA3ピンは5Vですので変数のRA3は1になります。またスイッチがONのとき、RA3ピンは0Vですので変数のRA3は0になります。
スイッチが押されるまで何もしない、ということは、変数RA3が1の間は次に進まないでずっと待ち続ける、というプログラムを書けばよいことになります。これは以下のようなプログラムになります。
while(RA3 == 1) {
}
なお、1はtrueを意味しますので、以下のように書き換えることもできますよね。
while(RA3) {
}
while文を忘れてしまった場合はC言語の本を読み返してみてください。
このプログラムをLEDの点滅処理の前に追加すればOKです。
完成したプログラム
以下は完成したプログラムです。コメントを入れましたので、処理の流れをよく確認してみてくだい。
なお、最初の作ったプログラムのLEDの点滅制御部分をちょっと変更しました。
理解したらこのプログラムをビルドして書き込み、動作確認してみましょう。一発で動かないこともあるかもしれません。その場合は諦めずに、ひとつひとつ問題がないか確認していきます。どうしてもわからない場合はコメント欄かお問い合わせページからご連絡いただければ、一緒に考えてみたいと思います。
/*
* File: main.c
* 変更履歴
* 2016.11.20: スイッチ制御部分を追加
*/
#include <xc.h>
// PIC12F1822 Configuration Bit Settings
// 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 = OFF // Power-up Timer Enable (PWRT disabled)
#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 = ON // 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 disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO // 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)
// クロック周波数指定
// __delay_ms()関数が使用する
#define _XTAL_FREQ 1000000
void main(void) {
// PICマイコン設定
OSCCON = 0b01011010; // 内部クロック周波数を1MHzに設定
ANSELA = 0b00000000; // すべてのピンをデジタルモードに設定
TRISA = 0b00001000; // すべてのピンを出力モードに設定(ただしRA3ピンは常に入力モード)
// LEDを点灯する
LATA5 = 1;
// スイッチが押されるまで待つ
while(RA3){
}
// LED点滅処理(永久に繰り返す)
while(1){
// LEDを950ms消灯する
LATA5 = 0;
__delay_ms(950);
// LEDを50ms点灯する
LATA5 = 1;
__delay_ms(50);
}
// 以下の命令は実行されない
return;
}
スイッチの扱い
スイッチの処理って思ったより簡単でしたよね。
PICマイコンのピンをデジタル入力にすると、ピンに対応する変数に電圧の状態に応じた値が入っています。また、スイッチを接続する場合、プルダウン抵抗かプルアップ抵抗を接続する必要があることも確認しました。
ということでこれだけの知識でスイッチを扱えるようになった感じがしますが、、、
残念ながらスイッチには根深い問題があります。この根深い問題については基礎編の最後の方か応用編で詳しく説明します。おそらくげんなりすると思いますが、頑張って理解しましょう。
疑問
今回までに、PICマイコンのピンを「デジタル出力」に設定した場合、ピンに対応する変数、例えばLATA5に値を代入するとピンの出力電圧を制御することを確認しました。
また、ピンを「デジタル入力」に設定した場合、ピンに対応する変数、例えばRA3の値を確認すると現在のピンの状態がわかりました。
この変数、ちょっと疑問があります。例えば、2番ピンのRA5で考えて見ます。2番ピンを「デジタル出力」に設定した場合、出力電圧を制御するには変数「LATA5」に値を代入することになります。同じ2番ピンを「デジタル入力」に設定した場合、ピンの状態を読み取るには変数「RA5」の値を確認することになります。
出力制御するときは「LATA」という名前で、入力の場合は「RA」という名前を使用していますが、これって統一できないんですかね。例えば、デジタル出力する場合は変数「RA5」に値を代入して、デジタル入力の場合は同じく変数「RA5」を読み取る、という感じです。
実は、今回作成したプログラムは全て「RA」変数を使用しても動作します。例えば、電源投入時に
LATA5 = 1;
としてLEDを点灯させましたが、この部分を
RA5 = 1;
としても動作します。
PICマイコンに関する書籍やサイトのプログラムを見ると、入力も出力も変数「RA」を使っているものがあります。特に問題が発生しない場合はこれで全く問題ありません。
ただ、例えばLEDを複数制御する場合などは、出力制御する場合「RA」を使用すると問題が発生するケースがあります。この問題については、基礎編の最後か応用編で詳しく説明します。
最初は、詳しい説明がないままで申し訳ありませんが、出力制御する場合は変数「LATA」、入力読み取りの場合は変数「RA」を使用する、と覚えておいてくだい。
更新履歴
日付 | 内容 |
---|---|
日付 | 内容 |
2016.11.20 | 新規投稿 |
2018.11.24 | プログラムテンプレートをMPLABX IDE v5.10に変更 |
ツール・ラボ様
はじめまして。
ツール・ラボ様のサイトからマイコンを始めました初心者です。
サイトの通りに回路をつくりプログラムもコピペしたのですが、
スイッチを押す前にLEDの点滅が始まってしまいました。
試しにスイッチを外し、
while(1){
LATA5 = RA3;
}
としてみたところ、RA3はどこにも繋がっていないにもかかわらず
LEDは不安定に点灯と消灯し始めました。
LEDが消灯している状態で指を近づけると点灯するなど反応がありました。
スイッチを付けて通電させると何故かLEDは高い確率で消灯になります。
他のPICシリーズのマイコンでも試しましたが同様の現象が発生します。
対処法をご教示頂ければ幸いです。
コメントいただきどうもありがとうございました。
実機での確認はできていないのですが、現象から考えてハードがノイズの影響を受けているような気がします。
そこで大変お手数なのですが、while文の記述を
while(1) {
}
と変更して試していただけませんでしょうか。この記述により、初期設定後このwhile文を永遠に繰り返す、つまりプログラム上では初期設定後何もしない、という状態になります。
これでもLEDが不安定な点滅をするようですと、電気的にどこかの端子が浮いている(=入力端子に何もつながれていない)状態が考えられます。
何か不明点ありましたらご質問いただければと思います。
ツール・ラボ様
ありがとうございます。
while(1) {
}
に変更したところ、LEDは全く点灯しなくなりました。
添付の画像の状態で行っているのですが、
間違っている部分がありましたらご指摘いただけると幸いです。
動作確認、ブレッドボードの画像、どうもありがとうございました。
ブレッドボードを確認しましたが、回路が間違っていますね。4番ピン(MCLR/RA3)とVDDの間のプルアップ抵抗がありません。この抵抗がないと、4番ピンがどこにもつながれていないことになり、今回のような現象が発生します。
回路の実装については第14回または第15回、プルアップ抵抗については第24回で解説しておりますので、ご確認いただければと思います。
プルアップ抵抗を接続するとプログラムはうまく動作すると思います。
ツール・ラボ様
プルアップ抵抗をつけることで解決できました。
理解不足、確認不足で質問してしまい申し訳ありません。
お忙しい中、本当にありがとうございました。
> なお、1はtrueを意味しますので
というのは正確ではないと思います。
trueは0以外の場合を指すわけですから、”1 はbool ではtrueなので”
というのが正解なのでは?
ご指摘どうもありがとうございます。
確かにfalseが0、trueが0以外ですので、「1はboolではtrueなので」の方が正確かもしれません。どうもありがとうございます。
お忙しい中すいません。
大阪に住むPIC初心者です。
SWによるLED点灯スタートのプログラムをコピペして動かしたのですがSWを押す前に点灯が始まってしまいます。
while(RA3){
}
が実行されていないようなのですが対処法を教えていただければ幸いです。
三浦秀信
三浦様
もう解決されたのかもしれませんが、
私も同じ現象となり悩みましたが、以下のようにして解決しました。
(原因)
おそらくPIC起動直後は、3番ピンの電圧が不安定なのだと思われます。
つまりまだ3番ピンにpull-upの5Vが出きらないうちに、whileの条件式が実行されて
しまい、ボタンが押されたと誤認識されたものだと思います。
(対応)
私はwhileに入る直前にdelayを入れることでうまく動作するようになりました。
__delay_ms(20); // 20msのdelayを追加し電圧の安定を待つ。
while(RA3) {
}
なお、原因は推測で、実際に3番ピンの電圧を測定したわけではありません。
万が一誤りやもっといい方法がある等あれば、どなたかご指摘いただければ幸いです。
その後、追試をしました。
Pickit(当方Pickit 4を使用)を外し、外部電源から直接5Vを給電した場合については20msのdelayは不要でした。従い、Pickit給電の場合に限りプログラム動作に5V給電が追いつかないケースがあるということと思われます。いずれ時間があれば実際に電源波形の計測も試みて見ようと思います。
情報の共有どうもありがとうございます。
私が確認したときは特に問題なかったのですが、PICKitの給電能力(状況)によってはRA3が不安定→while文を抜けてしまう、ということになるんですね。
外部電源の場合は問題なかったとのことですが、実際に運用する場合は電源不安定のケースも考慮して数十msのdelayを入れた方がいいのかもしれません。
貴重な情報でした。どうもありがとうございました。
ツールラボ様
解決しました。MCLRとRA3ピンは共有していますが、インプットとしてのRA3の機能をとりあえず物理的に外してPICKIT4でプログラムを書き込むとうまくいきました。考えてみればPICKIT4の機能はプログラム書き込みにあるのですから、インプット機能はそのあと配線しても問題ないですね。つまらないことお聞きして申し訳ありませんでした。
反応が遅く申し訳ございませんでした。
書き込みに問題がある場合、RA3ピンの機能を物理的に外す、というのが一番の解ですね。ただ、あとから配線する場合、ちょっと手間ですよね。自分も問題が発生した場合は同じ方法をとっていますが、他にもうちょっと楽な方法があるといいのですが…