HOME > 技術談話 > PIC® microcontroller > 1つのスイッチ
はじめに 事象のポイント 動作例
汎用ライブラリ サンプルプログラム ライブラリの応用
組み込み系の装置ではスイッチや表示装置の数が極端に少ない場合が多いです。 コストの問題もありますが、普段使用されないスイッチに関しては削減方向に仕様が設定されることが多いです。 例えば「デジタル時計」。時間を表示する部位は充実していますが、時刻合わせ用のスイッチの数は少ないです。多くても3個、少ない場合は1個。場合によっては通信で設定するから0個の場合もあります。 スイッチが少なくても目的とする操作を行うことはできますが、操作が困難な場合、面倒な場合はスイッチの使い方をプログラムで制御して機能を補います。例えばスイッチを長く押すと数字が連続して更新される様な動作です。 本項では少ないスイッチを使い分ける方法について説明します。 |
2010.02.15 新規作成 |
▲ |
(1)スイッチ連動 スイッチと制御が1対1で対応しているタイプです。 注意:図はチャタリング除去済みの状態で示しています。 |
常にスイッチの状態が反映されているイメージです。プログラムで制御するまでもない… シンプルなので表示切り替え用、ターゲットデバッグの状態表示等で使われることがあります。 |
(2)変化エッジ検出 スイッチの変化を検出して制御を行います。前述のスイッチ連動に似ていますが、変化点で制御を行います。 |
スイッチの状態が前回と異なる場合に制御を行います。少しだけプログラム制御になりました。 機能のオン、オフ制御で使われる基本パターンです。モータやランプ等の負荷制御で利用することが多いです。 |
(3)オンエッジ検出 エッジ検出と代わり映えしませんが、スイッチがオフからオンへの遷移だけに注目します。 |
スイッチの状態が前回と異なり、かつ、オン状態である場合に制御を行います。 値の更新、項目の選択で使われるパターンです。 |
(4)オフエッジ検出 スイッチがオンからオフへの遷移だけに注目します。 |
スイッチの状態が前回と異なり、かつ、オフ状態である場合に制御を行います。 この方法は単独で使用されることは少ないですが、値の更新、項目の選択で使われるパターンです。 この方法はパソコンアプリケーションのボタンクリックで利用されることが多いです。 |
(5)長押下検出 スイッチのオン状態が長時間にわたって継続している事に注目します。 |
スイッチがオン状態で t1 時間経過後に制御を行います。t1時間は秒単位の時間が多いです。 連続で値を変更する場合の開始条件、スイッチの誤操作抑止で使用される典型的なパターンです。 シンプルな方式ですが各種機器で多く利用されています。例えば、 ・連続して値を変更する場合のトリガ … 時計、項目選択 etc ・重要操作の動作開始 … 装置の動作オン、情報のクリア etc で利用されます。 |
(6)長押下継続 長押下検出の次の段階になりますが、スイッチのオン状態が継続している事に注目します。 |
長押下検出後 t2 時間周期で制御を行います。t2時間は数百ミリ秒単位の時間が多いです。 連続で値を変更する場合の継続条件で使用されるパターンです。 この方式は長押下検出とセットで使用されます。例えば、 ・連続して値を変更する … 短押下よりも高速に値を変更する etc で利用されます。 装置によっては長押下検出後、更に「長」長押下を検出し t2 時間を更に短く、もしくは値の更新単位を大きくする場合があります。が、度を過ぎると使いづらくなります。 |
(7)長押下終了 前述のオフエッジ検出と似ていますが、長押下検出以後のスイッチオフに注目します。 |
単なるオフエッジ検出と判断するよりも長押下検出以後のオフという判断の方が何かと融通が効きます。 判断を一歩進めて「長押下検出以後のオフ」と「長押下継続以後のオフ」という具合に区別することも可能です。が、細分化しすぎると判断が複雑になります(ほどほどが一番)。 |
2010.02.17 新規作成 |
▲ |
自動車のメータパネルに実装されるデジタル走行距離計(トリップメーター、オドメーター)について考えます。 |
左図はシンプルな仮想走行距離計です。構成部品は、 ・スイッチ … 1個 ・距離表示 ・積算(トリップ)、区間(オド)表示 です。 |
デジタル走行距離計の操作方法は自動車メーカーにより異なりますが、ほぼ以下の様な操作になります。 |
この操作方法を前述の事象と関連付け、状態遷移表で表現した場合は以下の様になります。 |
動作モード | 最初の作業 | オンエッジ検出 | オフエッジ検出 | 長押下検出 | 長押下継続 | 長押下終了 |
積算 | 積算表示 | 作業無し | 区間へ | 作業なし | 作業なし | 区間へ |
区間 | 区間表示 | 作業無し | 積算へ | クリアへ | あり得ない | あり得ない |
クリア | 区間クリア | あり得ない | あり得ない | あり得ない | 作業なし | 区間へ |
今回の場合、長押下検出までの時間は2秒程度を想定しています。 積算走行において長押下終了を有効にしていますが、これは長押下検出を無効にしているためです。 このような状態遷移表を用意しておくと定義のモレを抑止する効果が期待できます。 今回は走行距離計を例にしましたが、この考え方は時計の時刻設定や装置の保守用途で応用することができます。 |
2010.02.17 新規作成 |
▲ |
(1)はじめに 必要なスイッチの事象だけを選別して処理を作成しても構いませんが、毎回作ると飽きます。 そこで、スイッチの事象だけを専門に処理するライブラリを作成します。一度作成すれば今後開発する装置だけでなく、マイコンの種類を超えて再利用することができます。 |
(2)事象の種類 今回作成するライブラリでは以下に示す事象を取得できることとします。なお、時間管理(t1, t2)をライブラリに持ち込むと汎用性が低下するので、時間管理は上位関数で行い、ライブラリの中では実行回数(c1, c2)で管理します。 |
上図は通常のスイッチ操作であり、スイッチオフを経験してからの事象検出になります。 時としてスイッチオン状態で装置電源を投入し保守モードに移行する仕様があります。この場合は上図の事象検出の時間(回数)管理が成立しません。そこでライブラリ初期化時、既にスイッチがオンであった場合は一旦スイッチオフを経験するまでは通常のスイッチ操作では無い例外状態とします。その代わりに、当該状態からスイッチオフが検出された場合は認識可能を事象として通知することにします(通常のスイッチオフとは異なる扱いを推奨)。 |
事象 | 意味 | シンボル | |
(事象無し) | スイッチの変化、新規事象は検出されていない。 | SW_ACT_IDLE | |
認識可能 | スイッチオンの状態でライブラリ初期設定後、スイッチオフを検出。 | SW_ACT_READY | |
単純オン | スイッチオフからスイッチオンを検出。 | SW_ACT_ON | |
短押下確定 | 長押下確定前(c1未満)にスイッチオフを検出。 | SW_ACT_SHORT | |
長押下確定 | スイッチオンが c1 の期間継続。 | SW_ACT_LONG | |
長押下継続 | 長押下確定後 c2 の期間継続。以後、c2 期間継続していれば再度発行。 | SW_ACT_NEXT | |
長押下終了 | 長押下確定後のスイッチオフを検出。 | SW_ACT_END |
(3)ライブラリの構成 ライブラリは3つのファイルで構成されます。カスタマイズファイル以外は修正不要です。 C言語で記述されているのでPICマイコンに限らず他のマイコンでも利用することができます。 |
ファイル名 | 内容 | |
DrvSw.c | 本体です。チャタリング除去用の関数も含みます。修正不要。 | |
DrvSw.h | ヘッダファイルです。ライブラリを使用する前にインクルードします。修正不要。 | |
DrvSw.u | カスタマイズファイルです。一括処理するスイッチの数を指定します。 |
(4)ライブラリの導入 カスタマイズファイルを修正し、プロジェクトにライブラリ本体(DrvSw.c)を追加します。 ライブラリを利用するファイル側では本ライブラリのヘッダファイル(DrvSw.h)をインクルードします。 カスタマイズファイル(DrvSw.u)はヘッダファイルからインクルードされるので明示的なインクルードは不要です。 |
(5)関数説明 公開される関数は2つです(注:ライブラリの最新版ではプロトタイプ形式が異なります)。 |
項目 | 説明 | ||
関数名 | DrvSwActInit | 初期設定 | |
入力パラメータ | sSwAct *Info | 管理情報格納アドレス | |
size_sw Data | スイッチの状態(=0:オフ、≠0:オン) | ||
UH MaxLong | 長押下検出までの認識回数(1~) | ||
UH MaxNext | 長押下継続までの認識回数(1~) | ||
出力パラメータ | 無し | 無し |
項目 | 説明 | ||
関数名 | DrvSwActUpdate | 情報更新 | |
入力パラメータ | sSwAct *Info | 管理情報格納アドレス | |
size_sw Data | スイッチの状態(=0:オフ、≠0:オン) | ||
出力パラメータ | UB | 事象(前述のシンボル参照) |
上記以外にもチャタリング除去を始め、スイッチに関わる関数を用意しましたが本項での説明は省略します。 関数の使い方はシンプルです。2つの関数共に与えるスイッチの状態はチャタリング除去された値を指定してください。この時、ビット位置は問いませんが必ず1つのスイッチをマスクした状態でi指定します。 初期設定は1回。以後、情報更新(DrvSwActUpdate)を繰り返し呼び出しますが、その呼び出し間隔は一定時間とします。多くの場合 5ms~10ms の間になると思います。 |
(6)事象の利用 スイッチの状態、時間経過に応じて適した事象が返されますが、必要な事象のみをチェックするだけで大丈夫です。 前述の仮想走行距離計を例にすると、 |
動作モード | 認識する事象 | 遷移先 | 備考 | |
積算 | 短押下確定 | 区間 | ||
長押下終了 | 区間 | 結果的にスイッチオフを見ています | ||
区間 | 短押下確定 | 積算 | ||
長押下確定 | クリア | |||
クリア | 長押下終了 | 区間 |
の各事象だけをチェックすれば済みます。その他の事象は無視します。 |
(7)ダウンロード 次回説明するサンプルプログラムと共にライブラリを公開します。 |
2010.02.18 新規作成 |
▲ |
(1)テーマ スイッチ事象を取得するライブラリを利用したサンプルプログラムを作ります。 ハードウェアは別の項で解説したことのある PIC12F683 を利用したLED表示の回路を流用します。 今回作成するハードウェアでは3個の LED を装備しますが、この3個の LED をこれまでに例として挙げられた仮想走行距離計に見立てて制御します。スイッチは SW0 を利用します。 ・LED0 … 動作モードが「積算表示中」であることを点滅で示す。 ・LED1 … 動作モードが「区間表示中」であることを点滅で示す。 ・LED2 … 動作モードが「クリア中」であることを点滅で示す。 起動直後は LED0 が点滅していますが、 SW0 のオフエッジを検出して LED1 が点滅します。 LED1 が点滅している状態でオフエッジを検出すると LED0 が点滅します。 LED1 が点滅している状態で SW0 を2秒以上押下すると LED2 が点滅します。 LED2 が点滅している状態でスイッチをオフすると LED1 が点滅します。 注意事項 |
(2)回路図、部品の配置 LED表示で利用した回路図と部品の配置をそのまま利用します。 SW0は利用するので実装を忘れないでください(SW1は任意)。 |
(3)プログラム本体 (3)-1 01.00 サンプルプログラムを公開します … Test_One0100.zip HI-TECH C v9.70 で作成されたプログラムを v9.81 以降のコンパイラで再コンパイルする際の注意点 → 開発環境による修正 2011/03/09 スイッチ事象を処理する汎用ライブラリもファイルに含まれます。 ライブラリ本体はマイコンの種類を問わず汎用的に利用できるはずです。 が、実際作ってみて…作り直したい orz 今回、HI-TECH C V9.70 を利用してコンパイルしましたが、思いのほか容量を必要とします。今回のサンプルでは、 ・537/2048word(26.2%), 21/128byte(18.8%) 程度の容量を必要とします。データメモリはともかく、プログラム容量が予想に反して大きすぎます。展開具合を確認しましたがポインタを利用しているがために助長的な命令が生成されています。コンパイルログを見るとProモードを利用すれば 214word削減されるようです(537-214=323word)。 H8/SHマイコンならコンパクトに作成できるのですが orz ここで初心に戻ります。PICマイコンを利用するなら使用するスイッチの数は限られるはずです。 |
(3)-1 02.01 サンプルプログラムを公開します … Test_One0201.zip 実は先日作成しましたが、思うところがあって再度作成しなおしました。 HI-TECH C v9.70 で作成されたプログラムを v9.81 以降のコンパイラで再コンパイルする際の注意点 → 開発環境による修正 2011/03/09 前回(0100)の版では、 ・上位関数が変数を確保し、ライブラリにアドレスを渡す方式。 でした。これはこれで処理する変数が増えたときでもサブルーチン側の容量増加はありません。が、情報が1つだけの場合はプログラム容量に占めるスイッチ処理の割合が気になります。 今回(0201)の版では、 ・ライブラリ側が変数を確保し、上位関数はスイッチの状態を渡すだけ方式。 になりました。ライブラリ側で変数を直接編集するのでプログラム容量が削減されました。反面、処理する対象が増えた場合に対応できません。 なお、以前の動作も実現できる様、ユーザカスタマイズファイル(DrvSw.u)に宣言を用意しました。 色々実験する意味で処理の本体(main.c)では双方の方式が利用できるように作成しています。ユーザカスタマイズファイルを修正し、再コンパイルすることでプログラム容量とアセンブラへの展開状況を確認してみてください。 注意:方式の違いによりプロトタイプ宣言も変化します。 さて、どれだけプログラム容量が変わるのか? 以下にまとめてみました。 |
方式 | ポインタ方式 | ミニ関数方式 | |
プログラム容量(Liteモード) | 537word | 357word | |
プログラム容量(Proモード) | 323word | 215word | |
チャタリング除去関数容量 | 78word | 32word | |
スイッチ事象認識関数容量 | 227word | 127word |
利用したコンパイラはLiteモードです。Proモードにおける数字はコンパイラが表示する数字から計算した値です。 今回のサンプルプログラムではスイッチ事象の判断が「1つのスイッチ」だけでした。ライブラリの都合もありますが、スイッチ事象を判断する数が増えた場合、ミニ関数方式では倍々でプログラム容量が増えてしまうので、そのような場合はポインタ方式の利用を推奨します。 余談: ・間接アクセス命令が充実しているマイコン(例えば、H8/SH等)ではポインタ方式の方が便利です。 ・ミニ関数をアセンブラで最適化すればプログラム容量は更に小さくなりますが、PIC依存性が高くなってしまいます。 ・ライブラリの目標は汎用性の確保なので、今回は「マイコンの種類に依存しない」を優先しています。 ・スイッチ事象認識は…必要な機能に絞れば更に容量は小さく…これは利用する側の工夫です。 ・長押下の次の段階、長長押下が…2段階の速度アップを時には使いますが、利用する側の宿題ということで。 |
2010.02.21 新規作成 2010.02.22 ライブラリの修正(上位互換) |
▲ |
(1)チャタリング除去 ライブラリでは1個~32個までのスイッチを単位としてチャタリング除去を行います。マトリクス構成等を利用してスイッチの数を追加する場合はライブラリをポインタ方式で利用します(プログラム容量の関係)。また、システム仕様によりスイッチを個別にチャタリング除去を行うケースもあります。この場合もポインタ方式が有利です。 ライブラリは3つの関数で構成されます。 |
関数名 | 機能 | 説明 | |
DrvSwChtInit | 初期設定 | 管理情報単位で初期設定します。 | |
DrvSwChtUpdate | 情報更新 | スイッチの最新情報でチャタリング除去を行います。 | |
DrvSwChtGetResult | 確定情報取得 | 現在の確定情報を取得します。 |
ミニ関数を利用する場合は初期設定は1回で済みますが、ポインタ方式を利用する場合は管理情報ごとに初期設定を行います。ポインタ方式ではチャタリング除去回数を管理情報毎に定義できるので便利です。 初期設定時のスイッチの状態は即確定状態になることに注意してください。 各ビット毎のオン、オフの意味付けは上位関数で管理してください(スイッチ事象の判断関数では1でオン扱い)。 |
(2)ノイズ除去 要不要の判断が分かれるところですが、ノイズの多い環境下ではチャタリング除去の判定に時間を要する場合、あるいは判定ができない場合があります。 |
偶然、信号をサンプリングした時にノイズが加わり本来の状態とは異なる状態が認識されてしまった極端な例です。 このような場合、3通りの回避策が考えられます。 ・ハードウェアでノイズ対策を行う … コストは上がります。 ・チャタリング除去回数を多くする … スイッチの反応が遅くなります。 ・ノイズを積極的に除去する … 運が良ければ救済することができます。 今回は3番目の方法をライブラリに載せました。ノイズが数μs周期で繰り返し発生しないであろう(他力本願)という前提です。具体的には3回サンプリングした情報の多数決を得ます(サンプリングは連続短期間で行う必要あり)。 論理演算で処理しているので、高速に最大32種の信号を同時に多数決判断することができます。 要不要は別として、マクロ方式と関数方式の2通りを用意しました。 size_sw DrvSwMaj(size_sw Sw1, size_sw Sw2, size_sw Sw3); … これは関数方式 単発のノイズであればチャタリングの一致までに少しだけ時間を要するだけ、今回のノイズ除去を行ったからといってノイズを完全に除去できるわけでもなく…悩ましいのですが、ソフトウェアからみた「お守り」的な位置づけです。 |
(3)単純にオンエッジを見る これまで「スイッチ事象」という少々重めの処理を紹介しましたが、単純にオンエッジを見るだけであれば論理演算だけで高速に抽出することができます。 ライブラリにはマクロ方式と関数方式の2通りを用意しました。 size_sw DrvSwGetOnEdge(size_sw LastTime, size_sw Latest); … これは関数方式 前回がオフ(0)、今回がオン(1)として処理しています。オンエッジ対象ビットは「1」になります。 … オンエッジ = (前回 xor 今回) and 今回 処理終了後は今回の情報を前回の情報へコピーすることを忘れないでください。 |
(4)単純にオフエッジを見る 単純にオフエッジを見るだけであれば論理演算だけで高速に抽出することができます。 ライブラリにはマクロ方式と関数方式の2通りを用意しました。 size_sw DrvSwGetOffEdge(size_sw LastTime, size_sw Latest); … これは関数方式 前回がオフ(0)、今回がオン(1)として処理しています。オフエッジ対象ビットは「1」になります。 … オフエッジ = (前回 xor 今回) and 前回 処理終了後は今回の情報を前回の情報へコピーすることを忘れないでください。 |
(5)スイッチの変化を見る スイッチの変化を見るだけであれば排他的論理和だけで高速に抽出することができます。 これは簡単なのでライブラリでは定義していません。以下の例を参考に直接コーディングしてください。 前回がオフ(0)、今回がオン(1)として処理しています。変化対象ビットは「1」になります。 … 変化 = 前回 xor 今回 処理終了後は今回の情報を前回の情報へコピーすることを忘れないでください。 |
(6)スイッチの異常を検出するのは上位関数の役目 スイッチ表面に配置されたシールや筺体の構造がスイッチの変化を阻害するスイッチが効かない)場合があります。 スイッチの異常状態を検出する手法として、 ・装置起動時にスイッチがオン状態の場合は当該スイッチを無効とする。 ・オン状態が長時間継続する場合は当該スイッチを無効とする。 等が挙げられます。 今回スイッチ事象のライブラリを紹介しました。装置起動時のオン認識だけでなく、回数(=時間)監視を行っているので異常状態を認識することは容易なのですが、あえて異常判断は行っていません。これはライブラリ側でスイッチの用途、スイッチ同士の依存関係を判断できないからないからです。 例えば、オフスイッチが壊れている状態でオンスイッチを有効にするか否か。これはライブラリでは判断できません。 将来的に異常判断をライブラリに組み込むことは可能ですが…どうしたものでしょうか? スイッチの仕様次第。かな。 |
(7)スイッチ事象のメッセージ送信はリスクを伴う マルチタスクを利用している場合、認識されたスイッチ事象をメッセージで飛ばすことは可能です。ですがメッセージボックスの数は有限であり、ボックスフルが発生すると新たな情報が格納できなくなり処理が破綻します。 十分な量のメッセージボックスと、受信タスクのサービス頻度を上げることで問題は回避できますが、リスクを伴うことに注意してください。 |
(8)複数のスイッチをまとめて処理するとリスクを伴う 少なくとも物理的に異なる形式のスイッチはチャタリング除去を分けて行うべきですが、同種のスイッチを複数利用している場合はどうしましょう? 理想的なチャタリング除去を行うスイッチの単位は1個です。これは注目しているスイッチが他のスイッチの影響を受けない点で有利です。 反対に複数のスイッチをまとめて処理した場合はどうなるでしょう?以下に影響した場合の例を載せます。 |
処理時間やメモリ使用効率の関係でチャタリング除去をまとめて行う場合も多いですが、上図の様なリスクを伴うことに注意してください。 |
2010.02.24 新規作成 |
▲ |