第21回 プログラム解説 〜 メイン処理 〜

やっとLEDを点滅させる処理のメインパートの説明です!

本シリーズ記事の内容を改訂して、基礎編、応用編、実践編として以下のリンクに公開しています。以下のシリーズはさらにいろいろなPICマイコンの機能をご紹介しています!

PICマイコン電子工作入門 〜基礎編〜
PICマイコン電子工作入門 〜応用編〜
PICマイコン電子工作入門 〜実践編〜

今回の説明

発光ダイオード点滅回路を完成させるために以下の順序で説明しています。このエントリの説明は(3)「プログラムを作る」の部分の、プログラム作成のステップになります。

  1. 発光ダイオード(LED)を電池と抵抗のみで光らせる回路を組む
    PICマイコンの回路を組む前に、まずは電池、抵抗、発光ダイオードのみを使って、ブレッドポード上に回路を組んで発光ダイオードを光らせてみます。ここでは電池、抵抗、発光ダイオードの回路記号、回路図と、回路図からブレッドボードに組む方法を説明します。
  2. PICマイコンのベース回路を組む
    はじめの一歩の回路は、発光ダイオードを1秒に1回光らせるだけの回路です。この回路を組みます。
  3. プログラムを作る
    発光ダイオードを1秒に1回光らせるプログラムを作成します。
  4. PICマイコンに書き込んで動作させる
    作成したプログラムをPICマイコンに書き込んで動作させてみます。
  5. ベース回路にスイッチを追加
    発光ダイオードの点滅をスイッチで開始させるために、ベース回路にスイッチを追加します。これまでは発光ダイオードを光らせる、という出力制御をしましたが、PICマイコンで外部から信号を入力する方法を確認します。
  6. ベース回路にブザーを追加
    スイッチ付きの1秒に1回光らせる回路を作りましたので、ブザーを追加してタイマーを作ってみます。

 

これから説明する部分

前回までに、「コメント」「ヘッダファイルインクルード」「PICマイコンコンフィグレーション設定」の各部分について説明しました。残りは以下の「その他設定」と「メイン関数」です。なお、「その他設定」でひとつ#defineしていますが、これはメイン関数の__delay_ms()という関数を使うときに必要なものとなっています。そのため、「メイン関数」から説明を始めて、必要になったときに「その他設定」を説明します。

Prog structure

 

メイン関数内の構成概略

MPLABXのFileメニューから”New File”メニューを選択して、”C Main File”を作成すると、新規ファイルが作成されて自動的に以下のコードが記述されている状態になります。


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

  return (EXIT_SUCCESS);
}

このメイン関数内にプログラムを記述していくわけですが、内部クロックを使用して、PICマイコンのピンに何かをつなげて入出力制御する場合、ほぼ以下のような構成となります。

Main block

まず内部クロック設定です。PICマイコンコンフィグレーション設定で内部クロックを使用する設定をしましたが、あくまで「内部クロックを使用する」という設定だけでした。このメイン関数の中では、内部クロックの周波数を設定します。

次に、PICマイコンの各ピンの機能を設定します。具体的には、デジタルにするかアナログにするかの設定と、入力ピンにするか出力ピンにするかの設定です。この入門では、発光ダイオードのON/OFF制御、つまりデジタル制御をしますので、発光ダイオードを接続しているピンを「デジタル」の「出力ピン」という設定を行います。

ところで、「デジタル」と「アナログ」についてもう少し詳しく説明しておきます。なお、この説明では電源電圧を5Vとし、ピンの入出力設定は「出力ピン」とします。このようなときに、ピンの機能を「デジタル」にした場合、出力は、0Vか5Vのどちらか、つまりデジタルになります。またピンの機能を「アナログ」にした場合は、出力は0V〜5Vで、例えばこの間にある1.25V、などと途中の電圧を指定して出力することができます。これは入力の場合でも同様です。でも、ちょっと、あれっ?、って思いませんか? 「アナログ」のときは0V〜5Vまで設定できるのだから、0Vと5Vの2つを使用したいのであれば、「アナログ」で0Vと5Vのみを使えばいいのではないか、という疑問です。いちいち「デジタル」か「アナログ」かという設定はやめて、すべて「アナログ」設定にしてしまい、デジタルで使いたい人は、0Vか5Vのみを使用、アナログで使いたい人は0V〜5Vの可変電圧を使用、ということにしてしまえばよさそうですよね。そうすれば、いちいち「デジタル」か「アナログ」かなんて面倒な設定をひとつ減らすことができますよね。

ではなぜ「デジタル」と「アナログ」をわざわざ設定するかというと、この設定に応じてPICマイコン内部の回路をデジタル用、アナログ用に切り替えていて、この内部回路が異なることにより、ピンを制御する動作速度が「デジタル」と「アナログ」で異なっているためです。具体的には「デジタル」の方が「アナログ」よりも早く処理できます。もし「アナログ」設定のみにして、デジタルで使いたい人は0Vと5Vのみを使用する、という場合、逆に0Vと5Vしか使わないのに「アナログ」の遅い速度(といっても早いことは早いですが)に付き合わなくてはならない、というデメリットがでてきてしまいます。そのため「デジタル」か「アナログ」か設定するようになっています。

※補足: PICマイコンでアナログ出力に対応しているタイプもありますが、PIC12F683は対応していません。アナログ出力に似た制御ができるPWM制御というものができるようになっています。

ここまでの設定で、PICマイコンはクロック設定に従って動作を行い、また各ピンの機能も決まりますので、いよいよプログラムで制御することが可能となります。ただ、実際の動作処理に入る前に、使用するピンを初期状態に設定します。今回は発光ダイオードの点滅制御をしますが、その制御は1秒に1回光らせる、というものになります。そのため、電源投入直後は発光ダイオードは消しておく設定をします。また他の例では、何か処理をしているときは赤の発光ダイオードを点灯させ、アイドル状態のときは緑の発光ダイオードを点灯させる場合、ピンの初期設定部分では、赤の発光ダイオードは消しておき、緑の発光ダイオードは点灯させておく、という設定を行います。またプログラム内で使用する変数なども何か初期設定が必要でしたらここで設定します。初期設定ができたら、いよいよ動作処理となります。

一点補足しておきます。内部クロック設定やピンの機能の設定は、メイン関数の始めに設定しましたが、その後、動作処理をしている間でも設定した内容を変更することができます。例えばメイン関数の始めに内部クロック周波数を1MHzに設定したあと、例えば動作処理中にあまり処理することがないときに、内部クロック周波数を125kHzに設定する、ということもできます。クロック周波数が低いと、PICマイコンの消費電力が少なくて済むので、このような工夫ができます。

動作処理の部分はどのような制御をするかでかなり異なってきます。発光ダイオードの点滅制御ができたら、その後は点滅開始させるスイッチを付けて、さらにブザーをつけて鳴らしてみます。この動作処理の部分がどのように変わっていくか確認してみてください。

それではメイン関数の詳細を説明します。

 

内部クロック設定

内部クロックを使用する場合、周波数は “OSCCON” という変数(レジスタと呼びます)に値を代入することにより設定します。この変数は “xc.h”ファイル(からインクルードされるヘッダファイル)に定義されています。

この入門では内部クロックの周波数を1MHzに設定します。1MHzに設定するには

OSCCON = 0x40;

と設定すればよいのですが、なぜこのような値を設定するのか、他の周波数に設定するときはどうすればよいか説明します。ここから2進数と16進数がでてきますので、もし忘れてしまっていたら復習しておきましょう。

OSCCONは1バイト、つまり8ビットのレジスタで、以下のように構成されています。

Osccon

表の見方ですが、1行目は第何ビット目か、2行目はそのビットが読み取りか書き込みかの区別です。R/Wの場合、そのビットは読み取りと書き込みができます。Rの場合、そのビットは読み取り専用です。3行目は設定項目、4行目はその設定項目の意味です。それでは、一番上のビットから設定方法を説明します。

まず第7ビットですが、これは使用しません。

第6ビットから第4ビットは、この3ビットでひとつの意味を表していて、内部クロックの周波数を表します。この3ビットにどのような設定をするとどの周波数に設定できるか以下にまとめておきます。

第6ビット 第5ビット 第4ビット 周波数
1 1 1 8MHz
1 1 0 4MHz(デフォルト)
1 0 1 2MHz
1 0 0 1MHz
0 1 1 500kHz
0 1 0 250kHz
0 0 1 125kHz
0 0 0 31kHz

1MHzに設定する場合、第6ビットから第4ビットは、1 0 0、という設定になります。

第3ビットは、読み取り専用で、ここの値を読み取ることによって今使用しているクロックが内部か外部かがわかります。このビットが1のときは外部クロックで動作していることを示し、0のときは内部クロックとなります。これは先のエントリで説明しましたが、例えば2段階クロックスタートアップで、電源投入時は内部クロックで動作させて、その後外部クロックに切り替える場合、この値を読み取ることにより、今どちらのクロックで動作しているかわかります。例えば外部クロックに切り替わったら本格的な動作を開始する、という制御ができます。

第2ビットと第1ビットは、読み取り専用で、ここの値を読み取ることによってPICマイコンの内部クロックの状態を知ることができます。PICマイコン内部には実は2種類のクロック発振部があり、第2ビットは高周波クロックの状態で、第1ビットは低周波クロックの状態です。1のときは安定状態、0のときは不安定であることを表します。ただ、この値の使い方はよくわからないのですが、例えば、内部クロックの状態が悪かったら、外部クロックに切り替える、とかなんですかね。いー加減ですみません。。。

第0ビットは、どのクロックを使用するかの設定です。1のときは内部クロック、0のときはPICマイコンコンフィグレーション設定で設定したFOSCで指定したクロックになります。プログラム中でクロックを切り替えたりしないようであれば、この値は通常0で問題ありません。

ということで、内部クロックの周波数を1MHzに設定する場合、各ビットは以下のように設定することになります。なお、使われていないビットや読み取り専用のビットにも0か1を入れておく必要がありますので、あまり深い意味はありませんが0を入れておきます。

使われていないビット(第7ビット)はとりあえず0にしておいてもいいとして、読み取り専用のビット(第3ビット〜第1ビット)を0で書き込んでしまっていいのか、という疑問があるかもしれません。読み取り専用のビットに書き込むプログラムを書いても、回路的にそのビットには書き込みできないので、無視されるだけですのでご安心ください。

Osccon 1mhz

ここまで決まりましたので、あとは2進数の 0100 0000 を16進数に直して、つまり 0x40 にしてOSCCONに代入すればOKです。

OSCCON = 0x40;

他の例としては、内部クロックを8MHzにする場合は、

OSCCON = 0x70;

となります。またお時間があるようでしたら他の例もいろいろと考えてみて下さい。

 

ピンの機能設定

次に、PICマイコンの各入出力ピンの設定を行います。まず設定の説明をする前に、もう一度PIC12F683のピン名とその機能を説明します。何度も出てきてすみませんが、PIC12F682のピン名を確認します。

Pic12f683 pin name
(Microchip社の12F683データシートより引用)

ここから入出力ピンに関するピンとそのピンの機能名を抜き出すと以下のようになります。機能名が長いため右側と左側のピンが区別つくように位置をずらし、色を付けてあります。

Pic12f683 io pins

ちょっとややこしいのでじっくりいきましょうね。

まず入出力制御ができるピンは、2番ピンから7番ピンです。これらのピンはすべて同じ性質ではなく、ちょっとずつ異なります。まず “GPx” という名前のついたピンは、デジタル制御ができることを示しています。また “ANx”という名称のついたピンは、アナログ制御ができることを示しています。

また、各ピンとも入力か出力かを設定する必要がありますが、どのピンも入力にも出力にも設定できるわけではあまりせん。具体的には、リセット用のピンは入力のみで出力には設定できません。これは上のピン機能名では表現されていないので注意が必要になります。

この情報からまとめると、各ピンは以下の機能設定が可能、ということになります。

ピン番号 デジタル制御 アナログ入力 入出力設定
2 対応(GP5) 非対応 入力・出力
3 対応(GP4) 対応(AN3) 入力・出力
4 対応(GP3) 非対応 入力のみ
5 対応(GP2) 対応(AN2) 入力・出力
6 対応(GP1) 対応(AN1) 入力・出力
7 対応(GP0) 対応(AN0) 入力・出力

これらの設定ですが、まずデジタル/アナログ制御の設定は “ANSEL” 変数で行います。また入出力設定は “TRISIO” 変数で行います。それぞれ詳細に説明します。

各ピンのデジタル/アナログ制御の設定を行うには “ANSEL” 変数に値を代入します。ANSELは1バイト、8ビットの変数でそれぞれのビットは以下のような設定になっています。

Ansel

1行目、2行目は先程と同じです。3行目はANSELレジスタの各ビットの設定項目名、4行目はその意味です。

第7ビットは先ほどと同様、使用しません。

第6ビットから第4ビットは、AD変換を行う際のクロックの設定を行います。といってもよくわからないですよね。すみません、これはアナログ制御する時に設定が必要なのですが、入門の領域を超えてしまうのでここでは説明を省略いたします。なお、アナログ制御を使用しない場合、つまりデジタル制御のみの場合、ここには 0 0 0 を設定しておけば問題ありません。

第3ビットから第0ビットは、それぞれAN3ピンからAN0ピンまでのデジタル/アナログ制御の設定を行います。0を設定するとデジタル制御ピン、1を設定するとアナログ制御ピンとなります。

最初に製作した回路は、発光ダイオード1つを制御する回路ですので、使わないピンがあります。使わないピンの設定は、特に意味はありませんが、すべてデジタルにしておくことにします。ということで、各ビットの設定値は以下のようになります。

Ansel set

結局ゼロを指定することになりますので、ANSELの設定は

ANSEL = 0x00;

ということになります。

次に各ピンの入出力設定です。先ほど説明しましたが、こちらは “TRISIO” 変数で行います。この変数も1バイト、8ビットでそれぞれのビットは以下のような設定になっています。

Trisio

表題の意味は今までと同様です。各ビット、0を設定すると出力、1を設定すると入力となります。なお第3ビット、つまりGP3の設定は入力のみとなり、このビットは読み出し専用となっています。

Trisio set

今回はとりあえずGP3も含めて0、つまり全ピン出力設定とします。GP3は入力固定で読み出し専用のビットなので、0をいれておいても問題ありません。ということで、結局ゼロを指定することになりますので、TRISIOの設定は

TRISIO = 0x00;

ということになります。

これでクロック設定とピン機能設定が終わりました。かなり難しかったですよね。さらにちょっとややこしかったと思います。最初の回路は発光ダイオードの点滅制御だけですので、出力ピンとしての扱いしかしませんが、その後、スイッチを付けますので、後でこの設定を変更することになります。そのときにまた確認してみてください。

 

ピンの初期設定

これまでのコードで一通りPICマイコンの動作設定が完了しましたので、いよいよピンを制御して発光ダイオードを点滅制御します。発光ダイオードはPICマイコンの2番ピン、GP5に接続しました。これからこのピンを0Vにしたり5Vにしたりして発光ダイオードを点滅制御するのですが、その方法は簡単です。”GP5″という変数に0を代入すると、GP5ピンは0V、”GP5″に1を代入するとGP5ピンは5V出力となります。

まず動作処理が始まる前に、初期設定として発光ダイオードを消灯しておきますので、メイン関数の「ピンの初期設定」のところで、

GP5 = 0;

と記述しておきます。これでGP5ピンは0Vとなり、発光ダイオードは消灯状態となります。

 

動作処理

さて、いよいよ1秒に1回、発光ダイオードをピカッ、ピカッと光らせる処理です。実は、事前に電源をONにしてからどのように発光ダイオードを光らせれば「ピカッ、ピカッ」と見えるか検討してみました。結果、950ms(ミリ秒)消灯状態にして、50ms点灯させると、ピカッ、という感じになりました。ということで、以下のようにGP5を制御すればそのように光らせることができます。

Led pattern

ということで、動作処理部分は言葉で書くと、

a) 950ms消灯状態にする
b) “GP5 = 1;”にして発光ダイオードを点灯させる
c) そのまま50ms点灯状態にしておく
d) “GP5 = 0;”にして発光ダイオードを消灯させる
e) (a)に戻る

ということになります。まず、永久に処理を繰り替える場合、手っ取り早いのがwhile文を使う方法です。

while(1){
  処理
}

と書くと、while文の中の処理を永遠繰り返します。ということで、あとは処理の部分に(a)〜(d)を書けばOKです。(b)と(d)については、それぞれ “GP5 = 1;”、”GP5 = 0;”と書けばいいのでこれで解決です。(a)と(c)はどのように書けばいいのでしょうか。

時間待ちをすればいいので適当な命令を必要回数for文などで繰り返せばいいのですが、C言語の場合これが意外に難しいんです。ある一定の時間待ちをするには、まずC言語がどのPICマイコンの命令にコンパイルされて、その命令にどの程度時間がかかるか調べる必要があります。できないことではありませんが、ちょー面倒です。そんなこともあってか、時間待ちの関数が用意されています。

__delay_ms()

という関数です。”delay”の前にはアンダースコア2個、後には1個つきます。これは、ms(ミリ秒)指定でどのくらい時間待ちをするか、という関数です。例えば950ms待つには、

__delay_ms(950);

とすれば、950ms時間待ちしてくれます。ということで、(a)〜(e)の処理をC言語にまとめると、

while(1){
  __delay_ms(950);
  GP5 = 1;
  __delay_ms(50);
  GP5 = 0;
}

ということになります。これでプログラムが完成、といきたいところですが、__delay_ms()を使用する場合、この関数はクロック周波数が何Hzに設定されているかの情報を必要とするため教えてあげる必要があります。教えるには、_XTAL_FREQにクロック周波数(単位はHz)を#defineします。今回は1MHZ = 10000000Hzにしましたので、

#define _XTAL_FREQ 1000000

と定義してあげればOKです。この#defineはメイン関数の前に置きました。

 

プログラム完成ですが

ようやく回路とプログラムが完成しました。完成したところで、何ですが、実はこの回路もプログラムも正確な時間は計測できていません。数十分程度のタイマーでしたら実用的に使えると思いますが、1日とか計測するタイマーとしては改良が必要です。

具体的には以下の点で正確に時間が計測できていません。

なお、完成したプログラムは一度「第16回 プログラムをコピペして一度動作させてみる」で書き込み、動作させる方法を説明していますので、まだ動作させていない方はこの記事を参考にして動作させてみてください。

次回、PICマイコンに書き込んで動作させる、というテーマにしていますが、書き込む方法について、PICKit3から電源供給する方法と、回路の電源を使う方法の説明をしたいと思います。

 

更新履歴

日付 内容
2015.9.22 新規投稿
2018.12.3 新シリーズ記事紹介追加