チャタリングを防止して、スイッチ入力を完成させよう:イチから作って丸ごと学ぶ! H8マイコン道(9)(3/3 ページ)
今回は、マイコン制御プログラムの基礎中の基礎、スイッチ入力にかかわるC言語プログラミングについて詳しく解説する!
プログラムでチャタリングを防止するには
――ようやくプログラムが完成したと思ったのに、スイッチを何回か押すと、カウントが飛んでしまうという現象が起きてしまいました。健一君も頭を抱えています。そう、この問題は「チャタリング」と呼ばれるスイッチによって引き起こされる現象が原因です。ここでは、チャタリング対策について紹介していきます。
チャタリングとは、スイッチ接点の振動により、1回の入力にもかかわらず数回ON/OFFしたかのように、信号が発生してしまう現象をいいます。スイッチの接点は、機械的なものなので、ON/OFF時にこのような現象が生じてしまうのです。
チャタリングをタイムチャートで見てみると、図5のようなON/OFFの振動になります。前述のとおり、マイコンの処理速度は大変高速です。そのため、この振動を“スイッチが数回押された”ものとして処理してしまうのです。
さて、このチャタリングをどのように防止したらよいのでしょうか。その解答例を以下に示します(ソースコード3)。
/* SW1が押された回数を表示する。 * */ #define PCR1 (*((volatile unsigned char *)0xffe4)) #define PDR1 (*((volatile unsigned char *)0xffd4)) #define PCR5 (*((volatile unsigned char *)0xffe8)) #define PDR5 (*((volatile unsigned char *)0xffd8)) #define PCR8 (*((volatile unsigned char *)0xffeb)) #define PDR8 (*((volatile unsigned char *)0xffdb)) #define SW1 0x01 #define SW2 0x02 #define SW3 0x04 void wait(int); int main(void) { unsigned char LED[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; int count; PCR1 = 0x0; PDR5 = 0xfe; PCR5 = 0x03; PDR8 = 0xff; PCR8 = 0xff; count = 0; PDR8 = LED[count]; for (;;) { while ((PDR1 & SW1) != 0);/* SW1が押されていないとき待機 */ wait(1000); /* チャタリング対策 */ if (count < 9) count++; else count = 0; PDR8 = LED[count]; while ((PDR1 & SW1) == 0);/* SW1が押されているとき待機 */ wait(1000); /* チャタリング対策 */ } return 0; } void wait(int n) { while (--n); }
ソースコード3 チャタリング対策をしたプログラム |
- ソースコード3のダウンロード(swcount_03.lzh)
ここでもう一度、図5をご覧ください。このプログラムでは、図5の黄色で示した時間分、「wait」関数で時間を稼ぎ、チャタリングを読み飛ばしています。「wait」関数は、空のループにより、一定時間CPUの処理を浪費します。
これでチャタリング対策もばっちりだね。
晴子さん、今度はプログラムもちゃんと動いているよ!
フフッ。
健一君、うれしそうねー。
とっても教えがいがあるわ。
エヘヘッ。
それじゃ、お言葉に甘えて、ボクのプログラム(ソースコード1)で起きたリンクエラーについて、もっと教えてくださいよ。
そうね。
もう少しだけ、健一君に付き合ってあげるわ。
変数の記憶域はどこだ
――健一君のプログラム(ソースコード1)では、記述していないmemcpy関数が勝手に入っていました。なぜ、そのようなことが起こるのでしょうか? そこには、変数の記憶域をどのようにメモリに配置するかという、C言語の仕組みが絡んできます。
C言語では、関数の中で宣言される(staticなし)変数を、“自動変数”と呼びます。そして、この自動変数の記憶域は、“スタック領域”に設けられます。
スタック領域は、マイコンのRAMに位置しますから、記憶域が確保されたばかりの内容は“不定”です。そのため、初期値を持つ変数の場合、変数初期化のプログラムが挿入されるのです。
今回のプログラムの場合、
- (1)main関数が呼ばれる
- (2)char配列LEDの記憶域と、int変数countの記憶域をスタックに確保する
- (3)memcpy関数により、char配列LEDの初期値をROMからコピーする
- (4)main関数の処理がはじまる
となります。
さて今回は、memcpy関数のような標準Cライブラリ関数のリンクを、「-l」オプションを指定することで解決しました。
「-l」オプションで、「-lc」とすると、標準Cライブラリがリンクに加わります。また、「-lgcc」とすると、longとlongの割り算など、C言語の実行に必要なライブラリがリンクに加わります。さらに、H8マイコン用GCCは、H8マイコン・シリーズをサポートしているので、CPU指定のオプションも必要になります。一口に“H8マイコン”といっても、搭載されているCPUは数種類あります。ここでは、「-mh」オプション、「-mn」オプションを併せて指定することで、H8Tiny用のライブラリをリンクさせています。
コラム: static変数
どうだった?
制御プログラミングって、いろいろ大変でしょ。
はい。
いろいろ“ワナ”にはまったけど、今回もC言語のいい勉強になりましたよ。
そうねー。
マイコンプログラミングの世界は、本当に奥が深いわ。
健一君もまだまだ頑張らないとね!!
はい。
それじゃあ、今日はありがとうございました!
さーて、帰ろっと!
ちょっと、待ちなさい!
今日も例のアレ(宿題)を出すわよ!!
え〜。
またですか……。
キッ!
ヒッー。
お、お手柔らかに(涙)。
晴子さんからの宿題(3)
マイコン内蔵のタイマを使って、1秒間隔で「0」から「9」の数字を7セグメントLEDに巡回表示するプログラムを作ってね。くれぐれも時間は正確によ!
今回は――スイッチの状態をとらえる(レベル・センス)のか、スイッチのON/OFF変化をとらえる(エッジ・センス)のか――、スイッチ入力にかかわるプログラミングを解説しました。また、変数の記憶域がRAMかROMか、どこに、どのように確保されるのか、C言語プログラムの機能を解説しました。これらは、マイコン制御プログラミングの基礎中の基礎です。ここでしっかりとマスターしてください。
さて、次回は「H8マイコンの周辺機能・タイマ」を紹介します。併せて、「割り込みプログラム」についても解説する予定です。ご期待ください!(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.