第22回 LED点滅プログラム完成

LEDを点滅するプログラムを完成させます。

目次

今回の説明

PICマイコン電子工作入門の基礎編は、次のステップで説明しています。

この記事の説明は「❸ プログラムを作る」のうち、「LED点滅プログラムを完成させる」を説明します。

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

PIC12F1822データシートの入手

これからプログラムのメイン部分(main関数)を作成していきます。

今回はPIC12F1822のデータシートの読み方も説明します。

ただ、データシートを使ってゼロから説明するとかなり難しくなってしまいますので、最初は要点のみ説明して、そのあとにデータシートを読んでみる、という説明方法で進めます。

もしデータシートを読むことが難しかったら、とりあえず要点の説明のみを理解して先に進めていただければと思います。


最初はデータシートを読むのはかなり大変ですが、その内容構成(章立てなど)はどのPICマイコンでも大体同じです。

最初は読み解くのに時間がかかりますが、読めるようになると、違うPICマイコンで何か設計する場合でも、必要な設定などすぐに情報を得られるようになります。

また、可能であれば英語版のデータシートを見るようにして、よくわからなかったら日本語版を参照するようにしてみてください。というのは、PIC12F1822のように日本語版が用意されているマイコンもありますが、多くのPICマイコンではデータシートは英語版のみ提供されているためです。

それでは必要なデータシートをダウンロードして手元に置いておきましょう!

PIC12F1822データシート(PDFファイル/英語版)

PIC12F1822データシート(PDFファイル/日本語版)

今回の記事の説明部分

前回までの記事で、「コメント」「ヘッダファイルインクルード」「PICマイコンコンフィグレーション設定」の各部分について説明しました。

特にコンフィグレーション部分は眠かったですよね…。それに基礎という割にはちょっと難しかったような…。でも大丈夫です。ここまでのところは、今回製作するタイマーのように、電池で動作させ、内部クロックを使用するのであれば、「典型的な設定」で特に問題ありません。


今回説明する「その他設定」と「メイン処理」については、作るものによって大きく異なりますので、内容をできるだけ理解するようにしてみてください。

メイン処理内の構成概略

MPLAB X IDEで新規main.cファイルを作成すると、自動的に次のコードが記述されている状態になります。

void main(void) {
    return;
}

このメイン関数内にプログラムを記述していきます。

今回製作するタイマーに限らず、他の処理でもメイン関数はほぼ以下のような構成となります。

main関数基本構造

それぞれの部分について概要を説明します。

内部クロック設定

最初は 内部クロック設定 部分です。

プログラムのコンフィグレーション設定部分で、「内部クロックを使用する」という設定にしましたが、あくまで「内部クロックを使用する」という設定だけで、具体的な周波数は設定しませんでした。

内部クロック使用時、周波数はメイン関数の中のプログラムで設定します。

ピン設定

ピン設定 部分では、PICマイコンの各ピンの機能を設定します。

設定内容は、「デジタル制御にするかアナログ制御にするか」「入力ピンにするか出力ピンにするか」の設定です。

この基礎編では、LEDのON/OFFを制御、つまりデジタル制御をしますので、LEDを接続しているピンに対して「デジタル」の「出力ピン」という設定を行います。

また、この後スイッチを追加しますが、スイッチはOFF/ONの2つの状態、つまりデジタル値を読み取ります。スイッチを接続しているピンに対しては「デジタル」の「入力ピン」という設定を行います。

ここで「デジタル」と「アナログ」についてもう少し詳しく説明しておきます。


以下の説明ではPICマイコンの電源電圧を5Vとし、ピンの入出力設定は「入力ピン」とします。

ピンの機能を「デジタル」にした場合、ピンの入力はデジタル、つまり0Vか5Vのどちらかになります。

一方、ピンの機能を「アナログ」にした場合は、入力は0V〜5Vの間、例えば1.25V、などと途中の電圧をを読み取る(入力)することができます。

「そうなんだ」と、なんとなく納得してしまいそうですが、よく考えてみると、ちょっと、あれっ?、って思いませんか?

「アナログ」のときは0V〜5Vまで読み取るできるのだから、デジタルで0Vと5Vの2つを使用したい場合も、「アナログ」設定で0Vと5Vのみを使えばいいのではないか、という疑問です。

いちいち「デジタル」か「アナログ」かという設定はやめて、すべて「アナログ」設定にしてしまい、デジタルで使いたい人は、0Vか5Vのみを使用、アナログで使いたい人は0V〜5Vの可変電圧を使用、ということにしてしまえばよさそうですよね。そうすれば、いちいち「デジタル」か「アナログ」かなんて面倒な設定をひとつ減らすことができます。

ではなぜ「デジタル」と「アナログ」をわざわざ設定するのでしょうか。

理由は、「デジタル」か「アナログ」かの設定に応じて、PICマイコン内部の回路をデジタル用、アナログ用に切り替えるためです。内部回路が異なると、ピンを制御する動作速度や消費電力が異なります。具体的には「デジタル」の方が「アナログ」よりもずっと早く処理できて、消費電力も少なくなります。

もし「アナログ」設定のみにして、デジタルで使いたい人は0Vと5Vのみを使用する、という場合、デジタルとしてしか(0Vと5Vしか)使わないのに「アナログ」の遅い速度(といっても早いことは早いですが)に付き合わなくてはならない、というデメリットがでてきてしまいます。また処理に時間がかかる分、電池も多く消費してしまいます。

マイコンのように何かの装置に組み込んで電池で動作させる場合、電力消費を抑えることは重要です。内部回路を変える(=動作速度や消費電力を適切にする)ために「デジタル」か「アナログ」か設定するようになっています。

ピンの出力の初期設定

ここまでの設定で、クロック動作と各ピンの機能を設定しましたので、いよいよプログラムでピンを制御することが可能となります。

ピン出力の初期設定 部分では、使用するピンを初期状態を設定します。

LEDは1秒に1回光らせる制御をしますので、電源投入直後はLEDは消しておきます。

またプログラム内で使用する変数なども何か初期設定が必要でしたらここで設定します。初期設定ができたら、いよいよ動作処理となります。

メイン動作処理

メイン動作処理の部分はどのような制御をするかで大きく異なります。

基礎編では、最初にLEDの点滅制御を行い、その次はLEDの点滅開始させるスイッチを付けて、最後はブザーをつけて鳴らしてみます。

この動作処理の部分がどのように変わっていくか確認してみてください。

それではメイン関数の詳細を確認していきましょう!

メイン処理の詳細

内部クロック設定

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

「OSCCON」は「Oscillator Configuration」(発振モジュール設定)の略です。

なお、これから2進数と16進数がでてきますので、もし忘れてしまっていたら復習してみてください。


基礎編と応用編では内部クロックの周波数を1MHzに設定します。1MHzに設定するには2進数表記では次のようになります。

OSCCON = 0b01011000;

16進数表記では次のようになります。

OSCCON = 0x58;

なぜこのような値を設定するのか、また、他の周波数に設定するときはどうすればよいか確認していきましょう!


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

Pic basic 21 osccon

この表はPIC12F1822データシートの内容を簡略化したものです。

表の見方ですが、1行目はビット位置、2行目は設定項目、3行目はその設定項目の意味です。また4行目は電源投入時やリセット時のデフォルト値です。

それでは、具体的な設定内容を説明します。

SPLLEN(第7ビット)

まず第7ビットは、PLLを使用するかどうかの設定です。

早速、あれっ?って感じですよね。PLLの設定はコンフィグレーション設定にありました。ここにも使用するかどうかの設定があるのは不思議ですよね。

このSPLLENは「Software PLL Enable」の略で、このビットでPLLを使用する/しないの設定ができます。ただし、コンフィグレーション設定でPLLをONにした場合はこのビットの設定は無効で、常にPLLが使用されます(つまり周波数は常に4倍される)。コンフィグレーション設定でPLLをOFFにした場合、プログラム動作中にPLLをONにしたい場合は、このビットを1にするとPLLが有効になります。0を設定した場合はPLLは無効です。

基礎編と応用編では、PLLは使用しませんので0を設定します。

IRCF(第6〜第3ビット)

第6ビットから第3ビットは、この4ビットでひとつの意味で、内部クロックの周波数を表します。

設定名称は「IRCF」ですが、データシートでは「Internal Oscillator Frequency Select(内部発振周波数選択)」と説明されています(後でデータシートを確認します)。

なんだか設定名称と英語があってないですよね。「IRCF」は、「Internal RC oscillator Frequency」の略で、日本語に無理やり訳すと、「内部の抵抗とコンデンサを使用した発振モジュールの周波数」という感じでしょうか。(設定名称と英語は合わせた方がいいような…)

この4ビットにどのような設定をするとどの周波数に設定できるか以下にまとめておきます。

第6ビット第5ビット第4ビット第3ビット周波数
0000または131kHz
001031.25kHz (MF)
001131.25kHz (HF)
010062.5kHz (MF)
0101125kHz (MF)
0110250kHz (MF)
0111500kHz (MF)
デフォルト
1000125kHz (HF)
1001250kHz (HF)
1010500kHz (HF)
10111MHz (HF)
今回使用
11002MHz (HF)
11014MHz (HF)
11108MHz (HF)
111116MHz (HF)

1MHzの場合は、第6ビットから第3ビットは「1011」という設定にすればよいことがわかります。


ところで、すぐにお気づきかもしれませんが、31.25kHzとか、125kHzとか、他の周波数もそうですが、2回出てきてますよね。例えば250kHzに設定したい場合、「0110の250kHz (MF) 」と「1001の250kHz (HF) 」の設定があります。これって、どちらに設定すればよいのでしょうか。

結論としては、「 MF 」と「 HF 」の両方がある周波数では、基本的には「 MF 」を使用するようにします。ただし、プログラムによっては「 HF 」を選択した方が良いケースもあります。

この部分は基礎編の範囲を超えるかもしれませんが、少し補足しておきます。

周波数の右側の文字を見ると、「 MF 」「 HF 」の他に一番最初の31kHzのところに「 LF 」がありますよね。

最初に「 MF 」、「 HF 」、「 LF 」は何を意味しているか、ここから解明していきましょう。

いま作成しているプログラムでは、PICマイコンのコンフィグレーション設定部分で、クロック設定を「内部クロックを使用する」としました。

「内部クロック」に設定した場合、当然PICマイコンの内部にあるクロック発生モジュールで生成したクロックを使用するわけですが、実は内部のクロック発生モジュールは次の3種類あります。(ハードウエア的に3個あります)。

  • 低周波モジュール(Low Frequency = LF)
    31kHzの周波数を発生させるモジュールです。あまり精度は良くありません。ウォッチドッグタイマーなどに使用されます。
  • 中周波モジュール(Middle Frequency = MF)
    500kHzの周波数を発生させるモジュールです。工場出荷時に調整されますので、ある程度の精度は期待できます。プログラム実行に使用します。この周波数を元に、この半分の250kHz、さらに半分の125kHz、という感じで31.25kHzまでの周波数を生成します。
  • 高周波モジュール(High Frequency = HF)
    16MHzの周波数を発生させるモジュールです。こちらも工場出荷時に調整されますので、ある程度の精度は期待できます。プログラム実行に使用します。この周波数を元に、この半分の周波数である8MHz、さらに半分の4MHz、という感じで125kHzまでと、その半分の半分の31.25kHzの周波数を生成します。

周波数の右側の文字、「 MF 」などは、どの周波数モジュールを使用して生成しているか、を意味しています。例えば、「250kHz (MF) 」であれば、中周波モジュールの500kHzのクロックを元に生成している250kHz、という意味です。

クロックの発生元モジュールが違うとはいっても、結局は250kHzの矩形波です。同じ250kHzだったら何が違うのでしょうか。250kHzだったら片方だけでいいような気もします。

周波数は、数値が大きいほど(周波数が高いほど)と消費電力が多くなります。つまり、同じ250kHzを指定するにしても「250kHz (HF) 」は「250kHz (MF) 」よりも消費電力が多くなります。ということは、250kHzにしたい場合、消費電力の少ない「250kHz (MF) 」を使用した方がよいことになります。となると、ますます「250kHz(HF)」は必要なさそうですよね。

今回製作するタイマーのように、プログラム動作中は周波数を変更しない場合、「 MF 」と「 HF 」の2種類があれば、消費電力の少ない「 MF 」で問題ないかな、と思います。

一方でスリープモードなどを設けた場合、スリープ中は周波数は少ない方が消費電力は低くなります。当然、プログラム動作中はキビキビ動く必要があるので、ある程度高い周波数が必要です。このようにプログラム実行中に周波数を切り替えたい場合があります。

周波数の切り替えは、同じクロック発生モジュールであれば、一瞬で周波数が変更されます。例えば、「1MHz (HF) 」から「500kHz (HF) 」というように、高周波モジュールを使用する周波数同士の切り替えは一瞬です。

しかし、例えば「1MHz (HF) 」から「500kHz (MF) 」というように周波数切り替えに伴い、モジュールが変更される場合は、切り替えの時間がかかります。(ほんの少しですが…)

つまりクロック周波数を切り替える時に、なるべくクロック発生モジュールの切り替えがないように、同じ周波数でもMFとHFが用意されているわけです。

なお、スリープモードにする場合、一般的に一番低い周波数(31.25kHz)にします。HFの周波数は125kHzより低い周波数は途切れていますが、31.25kHzはスリープモードで使用する重要な周波数ですので、MFもHFも両方用意されています。


かなり込み入った説明になってしまいましたが、結論は「 MF 」があれば「 MF 」を使うのが無難かな、という感じです。

これで第6ビットから第3ビットまでの説明は終わりです。

SCS(第1ビット・第0ビット)

第2ビットは使用されていませんので、最後は第1ビットと第0ビットです。

この2ビットはクロック元の指定です。

「SCS」は「System Clock Select」です。(クロック元の指定ってコンフィグレーション設定でも出てきたような…)

第1ビット第2ビット意味
10または1内部クロックを使用
01Timer1を使用
00コンフィグレーション設定のFOSCに従う

「11」または「10」の「内部クロックを使用」と、「00」の「コンフィグレーション設定のFOSCに従う」はすぐにわかると思います。「01」の「Timer1を使用」ですが、これは基礎編、応用編では出てこない内容になりますので詳しい説明は省略します。

コンフィグレーション設定のクロック指定(FOSC)で「内部クロックを使用する」という設定をしましたので、「00」のコンフィグレーション設定に従う、の設定でもいいですし、「10」または「11」の内部クロックを使用、の設定でも大丈夫です。

通常はクロックの設定はコンフィグレーション設定で行いますので、「00」にします。

内部クロック設定のまとめ

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

Pic basic osccon 21 setting

プログラムにすると、以下のようになります。

OSCCON = 0b01011000;

なんだかこれだけの設定をするのに、大変でしたよね。

お疲れのところすみませんが、実際のデータシートを見てみます。

OSCCONの説明は、英語版データシートは65ページ、日本語版では69ページにあります。以下は英語版データシートの抜粋です。

(Microchip Technology社 PIC12F1822データシートより引用・加工)

今まで説明した内容を念頭に置いて、このデータシートを読んでみます。

最初のレジスタ構成の部分は、3行で構成さています。一番上の行は、OSCCONレジスタの各ビットの「読み書き属性」と「デフォルト値」の説明です。

読み書き属性の行の意味は、次のレジスタ構成の記号の意味に説明されています。この記号の意味を以下にまとめます。

記号意味内容
RReadable Bit読取り可能ビット
(この書き込みしても反映されない)
WWritable Bit書込み可能ビット
UUnimplemented Bit無効ビット
(読み出すと常にゼロ、書き込みしても反映されない)
uUnchanged値変更不可
xUnknown不定
-n /nValue at POR&BOR
Other Resets
R/Wなどのあとに書かれている「-0/0」などの数字は、スラッシュの左側の数字は電源投入時または電源低下リセット時のデフォルト値、スラッシュの右側の数字はその他のリセット時のデフォルト値
1Bit is set1がセットされる
0Bit is cleared0がセットされる

2行目は設定項目で、1ビットから構成されている項目もありますし(SPLLEN)、複数ビットから構成されているものもあります(IRCF)。

一番下の行は、一番左が第7ビット(bit7)、一番右が第0ビット(bit0)であることを示しています。

各項目の設定値では、それぞれの設定項目の簡単な意味の説明と、設定値が説明されています。

なお、設定項目の詳細な説明については、データシートの他のところで詳しく説明されています。


各設定項目の内容はすでに説明しましたので、ここではデータシートを読むポイントのみ説明します。

2番目の設定項目「IRCF」を見てみましょう。最初に「bit 6-3」とありますが、これはこの設定項目は第6ビットから第3ビットの4ビットで指定する、という意味です。

次に「IRCF<3:0>>」というのは、IRCF単独で見ると、第3ビットから第0ビットの4ビット構成になっている、という意味です。そのあとは、設定項目の意味と、設定値に対する意味が書かれています。

あとは、今まで説明した内容と突き合わせると、データシートのOSCCONのページがだいたいわかると思います。このOSCCONですが、PICマイコンによって微妙に異なりますので、他のPICマイコンを使用する場合、データシートのOSCCONのページを探して、このように読み解くようにしてください。

ピンの機能設定

次に、PICマイコンのピンの機能設定を行います。

PIC12F1822の各ピンの割り当ては次のようになっています。

Pic basic 22 pic12f1822 iopins

この図で説明している機能は、基礎編で出てくる内容のみに絞っています。実際には各ピンにはもっと多くの機能が割り当てられています。実際に各ピンにどのような機能が割り当てられているかは、最後にデータシートで確認します。

それでは、上の図についてもう少し詳しく説明します。

入出力制御ができるピンは、2番ピンから7番ピンです。つまり電源ピン(1番ピンのVDDと8番ピンのVSS)以外は基本的に入出力制御ができます。

ただし、これらのピンはすべて同じ性質ではなく、ちょっとずつ異なりますので、いくつかの観点から違いを説明します。

アナログかデジタルか

最初はアナログかデジタルか、という観点です。

「RA」という文字のついたピンは、デジタル制御ができることを示しています。ピンの機能を見ると、電源ピンを除くすべてのピン(2番ピン〜7番ピン)に対して、「RA0」から「RA5」が割り当てられていることがわかります。

「AN」という文字のついたピンは、アナログ制御ができることを示しています。アナログ制御ができるピンは、デジタルピンよりも少なく、「AN0」から「AN3」までの4ピンです。また、デジタルピンと番号がずれている点にも注意してください。

このように、デジタル/アナログ指定は全てのピンで自由に設定できるわけではなく、例えば2番ピンは「RA5」しかありませんので、デジタル制御しかできない、ということになります。

入力か出力か

次に、入力か出力か、という観点です。

まず、デジタルで使用する「RA」ピンは、基本的に入力と出力のいずれの設定も可能です。

ただし例外があります。4番ピンの「RA3」は入力しか設定できません。この例外はほぼ全てのPICマイコンで共通です。ほとんどのPICマイコンでは4番ピンには「RA3」が割り当てられていて、入力しか設定できません。

この基礎編では、2番ピンにLEDを接続して点滅制御しますので、2番ピンを「デジタル」に設定して、さらに「出力」に設定することになります。

また、アナログで使用する「AN」ピンは、全て入力になります。この「AN」という機能は、A/Dコンバータ(Analog-to-Digital Converter)と呼ばれている機能で、ピンのアナログ電圧値(Analog)をデジタル値(Digital)に変換する(Convert)機能です。

応用編ではこのA/Dコンバータの機能を使ってピンの電圧値を読んでみます。その場合はピンの機能を「アナログ」に設定にして、さらにそのピンを「入力」に設定することになります。


ちょっと疑問があるかもしれません。デジタル制御する場合は例外はあるものの入力と出力の指定ができます。一方でアナログ制御する場合は入力しか設定できません。ではアナログの出力制御はどうするのか、という疑問です。

実際、PIC12F1822にはアナログの出力制御ができるピンが1ピンあります(7番ピン)。このピンをアナログ出力制御するには、他のレジスタを使って設定します。基礎編、応用編とも、この設定は使用しませんが、アナログ出力する場合は、これから説明するデジタル/アナログ設定と入力/出力設定を組み合わせて設定できるわけではない、ということを頭の片隅に置いておいてください。

それではこれから、各ピンのデジタル/アナログ設定、入力/出力設定を行います。

ピン設定の概要

ピンのデジタル/アナログの設定は「ANSELA」レジスタで行います。ANSELAは「Analog Select(Port A)」の略です。直訳すると「アナログ選択」ですが、意味的には「アナログ/デジタルの選択」という感じです。

入出力設定は「TRISA」レジスタで行います。TRISAは「Tri-state(PortA)」の略です。「Tri-state」を直訳すると「3つの状態」という意味ですが、とりあえず入出力設定をするんだ、と覚えておきましょう。(興味のある方は、「3ステートバッファ」という電子部品がありますので、ネットなどで調べてみてください。TRISAはこの3ステートバッファの設定になります)


ところで、ANSELAにしても、TRISAにしても、最後に文字「A」がついています。これは「PortA」を意味しているのですが、これってなんなんでしょうか。

この「Port」(ポート)は、PICマイコンに限らず、一般的なマイコンでも同じ言葉、意味で使われています。(最後のAはあとで説明します)。「Port」は日本語では「港」という意味ですが、マイコンの「Port」も「港」の意味合いで使われています。

ちょっと話が逸れますが、大きめな港に行くと、どこからきたかわからないけど、なんとなく遠くの知らない国から来たような大型船が停泊してたりしますよね。そんなのを見ると、この港ってなんだか自分の知らない外界と繋がってるんだなぁ、なんて思いを馳せたりします。

マイコンの「Port」もそんな感じです。マイコンでピンの制御して外部と電気信号のやり取りをしています。マイコンから見ると、ピンの外側は外界です。その外界と繋がる部分は港みたいな感じ、というところでしょうか。

PICマイコンでは、1つの「Port」で8ビット、つまり8ピンの制御ができるようになっています。8ピン以上制御できるPICマイコンでは「PortA」だけでは足りませんので、「PortB」「PortC」が用意されています。

PIC12F1822の場合、外部制御できるピンの数は6本ですので、ピンを制御する「Port」はひとつ(8ビット)あれば十分です。ということで、PIC12F1822はPortは一つしかありません。「PortA」というように「A」をつけて区別する必要もありませんが、慣習的にPortが一つしか必要なくても「PortA」と呼んでいます。

制御できるピンが多いPICマイコンでは、「PortA」「PortB」「PortC」と3つのレジスタがありますが、この場合、「PortC」のアナログ/デジタル設定は「ANSELC」レジスタで設定します。

前置きが長くなってしまいましたが、これから「ANSELA」と「TRISA」を詳細に説明します。

ピンのアナログ・デジタル設定(ANSELA)

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

Pic bacic 22 ansel registor

アナログかデジタルかの設定は、アナログ/デジタルの設定ができるピンのみ必要ですので、RAポートでいうと、「RA0(7番ピン)」「RA1(6番ピン)」「RA2(5番ピン)」「RA4(3番ピン)」の設定のみになります。「RA5(2番ピン)」「RA3(4番ピン)」はデジタル制御しかできませんので、これらのピンに対する設定はありません。

基礎編では以下のように設定することにします。

Pic bacic 22 ansel register settings

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

ANSELA = 0b00000000;

16進数では次のようになります。

ANSELA = 0x00;

ピンの入力・出力設定

次に各ピンの入力・出力設定です。

この設定は「TRISA」レジスタで行います。この変数も1バイト、8ビットでそれぞれのビットは以下のような設定になっています。

Pic bacic 22 trisa register

各ビット、0を設定すると出力、1を設定すると入力となります。

覚え方ですが、「出力」は英語で「Output」なので、頭文字の「O(オー)」 が「0(ゼロ)」に似ていますので、「0」になります。「入力」は「Input」で、頭文字の「I(アイ)」が「1(いち)」に似ているので「1」になります。

第3ビット、つまりRA3の設定は入力のみとなり、このビットは読み出し専用となっています。なお、TRISAレジスタを設定するときにRA3に設定する値は0、1どちらを設定しても1のままです。(0を設定すると大変なことが起きる、とかではないです)

今回はとりあえずRA3ピン以外は出力設定とします。

Pic bacic 22 trisa register setting

TRISAの設定は次のようになります。

TRISA = 0b00001000;

ANSELAとTRISAのデータシート

最後に、ANSELAとTRISAについてデータシートを確認します。

ANSELAレジスタは英語版データシートのp.118、日本語版のp.127です。以下は英語版のANSELAレジスタの説明です。

(Microchip Technology社 PIC12F1822データシートより引用)

読み方は先ほどのOSCCONレジスタとほぼ同じです。ほとんど説明するところはないですが、最後の「Note1」(注記)に書いてあることを説明します。

これは、「アナログ設定にした場合、TRISAレジスタの対応するポートを入力設定にすること」という意味です。

先ほど説明したように、アナログ設定にするとA/Dコンバータの機能のピンになり、ピンの電圧を読み取る、という「入力」の機能になります。

そのため、TRISAレジスタで対応するピンを「入力」に指定する必要がある、ということになります。

TRISAレジスタは英語版データシートのp.117、日本語版のp.126です。以下は英語版のTRISAレジスタの説明です。

(Microchip Technology社 PIC12F1822データシートより引用)

こちらもほとんど説明するところはないと思います。もしわからなところがありましたらコメント欄でご質問いただければと思います。

これでクロック設定とピン機能設定が終わりました。かなり難しかったですよね。次はいよいよピンの制御です。

ピンの初期設定

これまでのコードで一通りPICマイコンの動作設定が完了しましたので、いよいよピンを制御してLEDを点滅制御します。

LEDはPICマイコンの2番ピンに接続しました。これからこのピンを0Vにしたり5VにしたりしてLEDを点滅制御するのですが、その方法は簡単です。

このピンはデジタル出力の「RA5」として使用する設定をしましたので、「RA5」という変数に0を代入すると、RA5ピンは0V、「RA5」に1を代入するとRA5ピンは5V出力となります。

このように「RA5」に0や1を代入するとピンのデジタル制御ができるのですが、実は、、、この変数を使用すると問題が発生するケースがあります。この問題についてはかなり込み入った話になりますので、一通り回路を作成した後に、実際に実験をして、その問題を確認してみます。


ここで説明もなく突然出てきて申し訳ないのですが、ピンのデジタル出力制御をする場合は、例えば「RA5」ピンであれば、「LATA5」という変数を使用します。

今回製作した回路で、LEDを点灯したい場合は、次のように書きます。

LATA5 = 1;

消灯したい場合は、次のように書きます。

LATA5 = 0;

デジタル出力制御ピンは「RA5」ですので、「RA5 = 1;」という書き方もできます。(実際に動作します)

でも「LATA5 = 1;」と書いてください、と言われると、悶々としてしまうと思います。

この「RA5」と「LATA5」については、基礎編の最後の方で詳しく説明します。今は、「デジタル出力制御」する場合は「LATA」を使う、と覚えておくことにします。

参考ですが、あとでブザーをRA4ピン(3番ピン)に接続します。ブザーをONにする場合は、LATA4 = 1;、OFFにする場合はLATA4 = 0;と書くことになります。


それでは、プログラムを書きましょう!

まず動作処理が始まる前に、初期設定としてLEDを消灯しておきますので、メイン関数の「ピンの初期設定」のところで、次のように書いておきます。

LATA5 = 0;

これでRA5ピンは0Vとなり、LEDは消灯状態となります。

動作処理

さて、いよいよ1秒に1回、LEDをピカッ、ピカッと光らせる処理です。

この記事を書く前に、実際にどのような時間間隔でLEDを光らせれば「ピカッ、ピカッ」と見えるか検討してみました。

結果、50ms点灯させると、ピカッ、と光る感じになりました。そこで、次のようにRA5を制御することにします。

Led pattern

ということで、動作処理部分は言葉で書くと、次のようになります。

  1. 950ms消灯状態にする
  2. LATA5 = 1;でLEDを点灯させる
  3. そのまま50ms点灯状態にしておく
  4. LATA5 = 0;でLEDを消灯させる
  5. ❶に戻る

ずっと処理を繰り返す場合、while文を使うと簡単です。

while(1){
  処理
}

このように書くと、while文の中の処理をずっと繰り返します。

あとは処理の部分に上の❶〜❹の処理を書けばOKです。

❷と❹については、それぞれLATA5変数の代入でOKです。

❶と❸はどのように書けばいいのでしょうか?

XC8では、次のような時間待ちの関数が用意されています。

__delay_ms(待ち時間);

「delay」の前にはアンダースコア2個、後ろには1個つきます。この関数は、引数に指定した数値をミリ秒として時間待ちをする関数です。

例えば950ms時間待ちをするのは次のようなコードになります。

__delay_ms(950);

これで処理の部分が書けますので、次のようにまとめてみました。

while(1){
  __delay_ms(950);  // 950ms時間待ち
  LATA5 = 1;        // LEDをON
  __delay_ms(50);   // 50ms時間待ち
  LATA5 = 0;        // LEDをOFF
}

これでプログラムが完成、といきたいところですが、__delay_ms()を使用する場合、この関数にクロック周波数が何Hzに設定されているか教えてあげる必要があります。

教えるには、_XTAL_FREQにクロック周波数(単位はHz)を#defineで定義します。

今回は1MHz = 1,000,000Hzにしましたので、次のような定義になります。

#define _XTAL_FREQ 1000000

この#defineはメイン関数の前に書きます。

プログラム完成ですが…

ようやく回路とプログラムが完成しました。

完成したところでこんなことを言うのも何ですが、実はこの回路もプログラムも正確な時間は計測できていないんです。

数十分程度のタイマーでしたら実用的に使えると思いますが、1日とか計測するタイマーとしては改良が必要です。

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

  • 内部クロックの誤差
    今回はPICマイコンの内部クロックを使用しましたが、内部クロックはある程度、誤差があります。正確に時間を計測する、つまり正確なクロック信号を得るには、時計用の32.768kHzの水晶発振子を使用したりする必要があります。ただ、タイマー程度でしたら内部クロックでも十分実用になると思います。
  • プログラムの誤差
    作成したプログラムでは、正確には1秒の処理を繰り返すようにはなっていません。while(1){}LATA5=1, LATA5=0、という処理はほんの少しだけ時間がかかります。__delay_ms()でぴったり1秒時間待ちしていますので、それにプラスして、whileLATA5代入の処理分時間がかかっていることになります。ただ、これらの処理にかかる時間は非常に短いのでタイマー程度でしたらこのような実装で問題ありません。

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

更新履歴

日付内容
2016.10.15新規投稿
2018.11.24プログラムテンプレートをMPLABX IDE v5.10版に更新
2019.6.20誤記訂正(OSCCONのクロックソースのtimer1使用設定の誤記訂正)
2025.4.10誤植訂正&内容補足
通知の設定
通知タイミング
guest
24 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
プログラム初心者
プログラム初心者
22 日 前

お尋ねします。
今回のを参考にして、LEDを4個を流れる点灯を作りたいと思っています
RA5~RA2のピンを使用してそれぞれのピンの点灯時間、消灯時間をずらすプログラムを
くめば大丈夫でしょか?
プログラムは全くのド素人なので
ご指導を、お願いします

プログラム初心者
プログラム初心者
返信  管理者
21 日 前

ご連絡ありがとうございます。
現在、トラック野郎の模型を作製していまして
電飾をLEDで再現したくてたどり着いたのが『ツール・ラボ』さんのHPなのです

イメージとして
●○○○→○●●○○○→○○●●○→○○○●●(●がOFF,○がON)
と流れる様にしたいのですがプログラム初心者な物でLED1つは何とか
理解できたのですが、とはいってもHPの例だけですが
そこからはほぼ真白な状態です。。。。
お手数おかけしますが全体のプログラムの組み方を押してて頂けますでしょうか。
宜しくお願い致します。

プログラム初心者
プログラム初心者
返信  プログラム初心者
21 日 前

お世話になります。
下記の様なプログラムを考えてみました。
ON OFFのみですがどうでしょうか?

while(1){
__delay_ms(500); // 500ms時間待ち
LATA1 = 1;       // LEDをON
__delay_ms(2000);  // 2000ms時間待ち
LATA1 = 0;       // LEDをOFF }

while(2){
__delay_ms(1000); // 1000ms時間待ち
LATA2 = 1;       // LEDをON
__delay_ms(1000);  // 1000ms時間待ち
  LATA2 = 0;       // LEDをOFF }

while(3){
LATA3 = 1;       // LEDをON
__delay_ms(500);  // 500ms時間待ち
LATA 3= 0;       // LEDをOFF
__delay_ms(1000);  // 1000ms時間待ち
LATA 3= 1;       // LEDをON
__delay_ms(500);  // 500ms時間待ち LATA = 3;       // LEDをOFF }

while(4){
LATA4 = 1;       // LEDをON
__delay_ms(1000);  // 1000ms時間待ち
LATA4 = 0;       // LEDをOFF }

while(5){
LATA5 = 1;       // LEDをON
__delay_ms(1500);  //1 500ms時間待ち
LATA = 5;       // LEDをOFF __delay_ms(500);  //500ms時間待ち }

ご指導宜しくお願い致します。

プログラム初心者
プログラム初心者
返信  管理者
21 日 前

色々とご指導有難うございます。

LEDの動作を個別に指示をすのでなく、一連のLEDの動作をwhile(1)で支持(流れ)を指定し囲むのですね

個別に指示をして点灯のタイミングをずらし、流れを作ると
思っていました。

ご指摘のようにwhile(1)で組みますと、とてもシンプルで
わかりやすいです。点灯のパターンと速度もこの組み方ですと
応用が簡単に変更が可能ですね。

では第17回 ブレッドボード回路の動作確認を参考に書き込み作業を頑張ってみます。

色々とお手数をおかけしますが宜しくお願い致します。

プログラム初心者
プログラム初心者
返信  管理者
20 日 前

ご返答有難うございます。

#define INTERVAL 1000の定義はとても便利です

第17回の「プログラムのコピペ」の記事で36、37行の//LEDを消灯する LATA1 =0;の
下に指示をすればよろしいのでしょうか?

そして41行目から今回のプログラムの内容に変更すれば大丈夫でしょうか?

プログラム初心者
プログラム初心者
返信  プログラム初心者
20 日 前

たびたび済みません
プログラムを確認したところ
ピンの入力・出力設定ですがRA3 PINは入力のみLEDの接続はダメとなるのでしょうか
LED5個の接続ですとRA3以外
RA0~RA5の5つに接続するプログラムを
組めば大丈夫でしょうか?

プログラム初心者
プログラム初心者
返信  管理者
19 日 前

ご連絡ありがとうございます。

書き込みと回路を分けて作業を行ってみます。

LED点灯のマイコン制御は凄い技ですね
とても幅広く活用が出来そうです。

ハードは得意なのですが
ソフトは初めてなので色々と試してみます。

プログラム初心者
プログラム初心者
返信  管理者
16 日 前

ご指導ありがとうございました。

無事に書き込み動作、出来ました。
まずはHPのサンプルを参考に2個の点灯の
プログラムを組んでみました。

今回は色々とお手数おかけしました。
本当に有難う御座いました。

k2t
k2t
1 年 前

初めまして。
大変わかりやすい解説で参考にさせていただいています。

今回初めてのPICマイコンでLチカを試しているのですが、どうもうまく動きません。
行き詰ってしまったため、お助けいただけると幸いです。

使用しているマイコンは「PIC18F14K22」で
MPLAB X IDEのMCCで初期化コードを生成しています。

アルゴリズムは下記のようにしています。
・タイマ0を使って1msecの周期割込みを発生
・割込み処理内で1msecカウンタgMSecTimeをインクリメント
・mainループで1msecカウンタgMSecTimeを監視
・1000msec経過したら、LED(IOポートRA2)をトグル

発生している事象
・1000msec毎のトグルが、ときどき800msecくらいになってしまう。

—以下、ソースコード—————
uint16_t gMSecTime;

// タイマー割込み関数
void Timer0IntFunc(void)
{
  gMSecTime++;
}

/*
             Main application
 */
void main(void)
{
  uint16_t t_led_tgl = 0;   // time scale for LED   
  uint16_t diff;
   
  // Initialize the device
  SYSTEM_Initialize();

  TMR0_SetInterruptHandler(Timer0IntFunc);  // タイマー0割込み関数の登録

  // Enable the Global Interrupts
  INTERRUPT_GlobalInterruptEnable();

  // Enable the Peripheral Interrupts
  INTERRUPT_PeripheralInterruptEnable();

  t_led_tgl = gMSecTime;
   
  while (1)
  {
    diff = gMSecTime – t_led_tgl;  // 経過時間計算
    if( diff >= 1000 ){       // 1000msec経過?
       
      // ログ出力
      sprintf(line_buf, “%u,%u,%u\r\n”, gMSecTime, t_led_tgl, diff );
      TxString(line_buf,strlen(line_buf));
       
      IO_RA2_Toggle();  // LEDトグル

      t_led_tgl = gMSecTime; // 時間再計測
    }
     
  }
   
}

—以下、ログ出力—————————–
11195,10195,1000
12216,11216,1000
13056,12237,1074 ★この時、時間が短くなる。
14077,13077,1000
15098,14098,1000
16119,15119,1000

★の箇所で
gMSecTime:13056
t_led_tgl:12237
なので、
diff:819
となり、ifの条件に引っかからないはずなのですが
diff:1074
となっておりifの条件に引っかかってトグル処理を実行しています。
引き算の結果が時々おかしくなる!?

気になっているのは、
PIC18Fは8bitマイコンですがgMSecTimeが16bitなので
何か弊害があるのでは・・・
と思っています。

長々と申し訳ありません。
先人たちの考えをお聞きしたいので
よろしくお願い致します。

k2t
k2t
返信  管理者
1 年 前

返信ありがとうございます。
ログまで見ていただいて嬉しい限りです。

結果ですがvolatileを付けていても症状は変わりませんでした。

不躾ですがteratailにも同じ質問をしてみました。
https://teratail.com/questions/p4m6rtstnmgcuv

解決策ですが、下記の処理を割込み禁止にすることで正常動作しました。
diff = gMSecTime – t_led_tgl;  // 経過時間計算

簡単にいうとgMSecTimeは16bit変数ですがマイコンは8bitなので
下位バイトと上位バイトを読み込む間に割込みが入ると計算が異常になる
という事象が発生していたと思われます。

これまで組込みで32bitCPUしか扱った経験が有りませんでしたので
8bitや16bitCPUはこんなケアもしなきゃならないのか・・・
と少し戸惑いました。

たかがLチカでも侮れないと痛感・・・

これからも参考にさせていただきます。
ありがとうございました。

Gemini_mk2
Gemini_mk2
2 年 前
  1. 950ms消灯状態にする
  2. 「LATA5 = 1;」でLEDを点灯させる
  3. そのまま50ms点灯状態にしておく
  4. 「LATA5 = 0;」でLEDを消灯させる
  5. (1)に戻る

1~5ではなくてa~dではないでしょうか?

tomh
tomh
5 年 前

内部クロックOSCCONの設定で「10」の「Timer1を使用」の表記がありますが、「10」とは1ビット目と0ビット目と言う意味で良いでしょうか、前の説明は設定値なので「01」では?と勘違いしそうな・・・
でも、とっても素人に優しく説明して頂き非常に感謝しています。
これからも是非サポート宜しくお願いします。

管理者
管理者
返信  tomh
5 年 前

ご指摘どうもありがとうございました!
すみません、「timer1を使用」は「01」(第1ビットは0、第0ビットは1)になります。本文訂正しておきました。自分でも記事見直しをしているのですが、気づきませんでした。どうもありがとうございました。

このシリーズは、自分がPICマイコンを手がけた時にいろいろと苦労したので、過去の自分に対して書いている感じです。なるべく分かりやすく書いているつもりですが、分かりづらいところは多々あると思います。そのような時はまたご質問いただければと思います。

musashi
musashi
8 年 前

>>今回は1MHZ = 10000000Hzにしましたので、
この部分、桁が1つ多いのではないでしょうか。

管理者
管理者
返信  musashi
8 年 前

musashiさま、
ご指摘どうもありがとうございます!

すみません、間違っていました。カンマで区切って修正しました。

自分では見直しても気づかないと思いますので、ご指摘いただいて助かります。どうもありがとうございました。

目次