チャタリングを防止して、スイッチ入力を完成させようイチから作って丸ごと学ぶ! H8マイコン道(9)(3/3 ページ)

» 2009年12月16日 00時00分 公開
前のページへ 1|2|3       

プログラムでチャタリングを防止するには

――ようやくプログラムが完成したと思ったのに、スイッチを何回か押すと、カウントが飛んでしまうという現象が起きてしまいました。健一君も頭を抱えています。そう、この問題は「チャタリング」と呼ばれるスイッチによって引き起こされる現象が原因です。ここでは、チャタリング対策について紹介していきます。

 チャタリングとは、スイッチ接点の振動により、1回の入力にもかかわらず数回ON/OFFしたかのように、信号が発生してしまう現象をいいます。スイッチの接点は、機械的なものなので、ON/OFF時にこのような現象が生じてしまうのです。

 チャタリングをタイムチャートで見てみると、図5のようなON/OFFの振動になります。前述のとおり、マイコンの処理速度は大変高速です。そのため、この振動を“スイッチが数回押された”ものとして処理してしまうのです。

スイッチ入力によるチャタリングの発生 図5 スイッチ入力によるチャタリングの発生

 さて、このチャタリングをどのように防止したらよいのでしょうか。その解答例を以下に示します(ソースコード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 チャタリング対策をしたプログラム

 ここでもう一度、図5をご覧ください。このプログラムでは、図5の黄色で示した時間分、「wait」関数で時間を稼ぎ、チャタリングを読み飛ばしています。「wait」関数は、空のループにより、一定時間CPUの処理を浪費します。


photo

これでチャタリング対策もばっちりだね。


晴子さん、今度はプログラムもちゃんと動いているよ!


photo

フフッ。


健一君、うれしそうねー。


とっても教えがいがあるわ。


photo

エヘヘッ。


それじゃ、お言葉に甘えて、ボクのプログラム(ソースコード1)で起きたリンクエラーについて、もっと教えてくださいよ。


photo

そうね。


もう少しだけ、健一君に付き合ってあげるわ。



変数の記憶域はどこだ

――健一君のプログラム(ソースコード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変数

今回は、memcpy関数をリンクすることで、実行プログラムを作りましたが、そもそもmemcpy関数を使わないC言語プログラムの記述はできないのでしょうか? ここでは、静的変数(static変数)として変数を宣言する方法を紹介します。

関数内で変数宣言するとき、記憶クラス指定子「static」を付けると、その変数には静的記憶域が確保されます。

例として、

static unsigned char LED[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

とすると、char変数LEDは静的変数になります。

静的変数は、プログラムの実行開始時に記憶域が確保され、1度だけ内容が初期化されます。

プログラム中でchar変数LEDに値が代入されることはないため、char変数LEDは、“読み取り専用(Read Only)”と解釈しても差し支えありません。ですからCコンパイラは、char変数LEDをROMに配置します。ちなみに、char変数LEDをグローバル変数としても同じ効果が得られます。



photo

どうだった?


制御プログラミングって、いろいろ大変でしょ。


photo

はい。


いろいろ“ワナ”にはまったけど、今回もC言語のいい勉強になりましたよ。


photo

そうねー。


マイコンプログラミングの世界は、本当に奥が深いわ。


健一君もまだまだ頑張らないとね!!


photo

はい。


それじゃあ、今日はありがとうございました!


さーて、帰ろっと!


photo

ちょっと、待ちなさい!


今日も例のアレ(宿題)を出すわよ!!


photo

え〜。


またですか……。


photo

キッ!


photo

ヒッー。


お、お手柔らかに(涙)。



晴子さんからの宿題(3)

マイコン内蔵のタイマを使って、1秒間隔で「0」から「9」の数字を7セグメントLEDに巡回表示するプログラムを作ってね。くれぐれも時間は正確によ!





 今回は――スイッチの状態をとらえる(レベル・センス)のか、スイッチのON/OFF変化をとらえる(エッジ・センス)のか――、スイッチ入力にかかわるプログラミングを解説しました。また、変数の記憶域がRAMかROMか、どこに、どのように確保されるのか、C言語プログラムの機能を解説しました。これらは、マイコン制御プログラミングの基礎中の基礎です。ここでしっかりとマスターしてください。

 さて、次回は「H8マイコンの周辺機能・タイマ」を紹介します。併せて、「割り込みプログラム」についても解説する予定です。ご期待ください!(次回に続く)


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.