Release 2017.6.26 / Update 2017.7.4

「B2CS」Arduino用自作ライブラリプログラミングでチャタリング回避

“ゲージ判定”を使ったチャタリング回避ライブラリを作ってみました。Arduino用です。

こんな事ができます。

以前、タクトスイッチを使いArduinoでMIDIを鳴らす方法を紹介しましたが、原理は一緒です。プログラミングだけでチャタリングを回避し、複数のスイッチを手軽に扱えるようにします。

見よう見真似で作ったライブラリなので、コードの稚拙さは勿論、まだ使い倒してないため不具合があるかもしれません。それでも興味がある方は試してみてください。

始めに

B2CSライブラリの特徴

このライブラリの目的は、Arduinoでのモーメンタリスイッチ使用時、物理的に起きる“チャタリング”をプログラミングだけで回避することです(完全ではありませんが…)。仕組みは単純で、digitalRead一回の読み取りでなく、何回か読みに行った統計を“ゲージ”として捉え、一定量を超えたときにONとみなす“ゲージ判定”によるスイッチ判別方法です。また、以前の記事で紹介したとおり、“ゲージ判定”によって、ついでに可能になった「長押し」の判別も出来るようになっています。

そして、初期設定では最大8個までのスイッチを個別に判定できる仕様にしています。

未完成

普段スケッチを書くのにClassを使うことも無ければ、勉強もしていませんので、ソースコードがひどい状態です。無駄な関数や変数も多く、容量がかさばっています。

多分、判る人が見れば「なんじゃこりゃ」と思うでしょう。無知な自分でさえ、「Classはこう使うものじゃない」と確信しています。ですが、整理・修正していこうとなると、勉強しつつになるので、だいぶ先の長い話になってしまいます。

なので、このライブラリは「ベータ版」という事でご了承ください。

ただ、そのマイナス点を考慮しても、十分機能はすると思うので、特にArduino初心者の方(自分もまだまだ初心者レベルですが…)には重宝してもらえるのでは、と。

B2CSライブラリの導入

B2CSライブラリでは「タイマー割り込み」を使って、頻繁にスイッチの状態を確認しています。その機能にはMsTimer2というライブラリを使っているので、ArduinoIDEにインストールされている事が前提となります。現行のIDEなら標準で組み込まれているかもしれませんが、もし入っていないなら下記を参考にインストールしてください。

*MsTimer2使用時は3, 11番ピンのPWMを使えなくなるようです

MsTimer2ライブラリのインストール

IDEのプルダウンメニュー「スケッチ」の「ライブラリをインクルード」から「ライブラリを管理」で“ライブラリマネージャー”を開きます。

検索で“MsTimer2”と入力。「INSTALLED」ならOK。そうでなければインストールしてください。

インストール後は念のため、一度IDEを終了して、立ち上げ直してください。

ライブラリZIPファイル

こちらのファイルをダウンロードしてください。

version 日付 修正内容
B2CS.ZIP – version 1.0.2 2017.6.26 bug fix
B2CS.ZIP – version 1.0 20176.25  

インストール

Arduino IDEプルダウンメニュー「スケッチ」の「ライブラリをインクルード」から「.ZIP形式のライブラリをインストール」を選択。ダウンロードしたZIPを選択してください。

こちらも念のため、一度IDEを終了して、立ち上げ直しします。

使い方

まずは一番シンプルな使い方で、順を追って説明します。

サンプルスケッチ1

回路図

必要なのはモーメンタリスイッチ1個だけです。

1.ライブラリのインクルード

インストールした”B2CS”ライブラリを読み込みます。

2. インスタンス作成

関数外でインスタンスを作成。名称は任意でOKですが、ここでは小文字で“jumbleat_sw”にします。

B2CS jumbleat_sw;

3.ピンのアサイン

スイッチとして使うピンの設定をします。設定には.setSW()という関数を使います。

.setSW( ピン番号、INPUT/INPUT_PULLUP、スイッチON理論値 );

3つの引数が入りますが、最初の2つはpinMode()と一緒です。3つ目には、スイッチがONになる時の理論値を入れます。

この例では、2ピンへ内部INPUT_PULLUP接続したいので、

jumbleat_sw.setSW( 2, INPUT_PULLUP, false );

となります。

例えば、扱うスイッチを増やしたければ、この関数を再度呼び出すだけです。

jumbleat_sw.setSW( 2, INPUT_PULLUP, false );
jumbleat_sw.setSW( 3, INPUT_PULLUP, false );
.
.

単純です。

4.タイマー始動

ピンの設定が終わったら、割り込みをスタートさせます。関数は.GO()です。

jumbleat_sw.GO();

Setup()内の最後の方で呼び出します。

5.状態を読み取り

後は、loop()内等でスイッチの状態を読みに行って、分岐命令を書くだけです。ピンの状態を見るには.read()関数を使います。

.read( ピン番号 );

こちらの関数には下記の返り値があります。

define定義文字列 数値 状態
– – – – – 0 押されていない
SW_PUSHED 1 スイッチが押されている
SW_PUSHED_LONG 3 長いことスイッチが押されている
SW_RELEASED 5 スイッチを離した
SW_RELEASED_LONG 7 長いこと押した後スイッチを離した

つまり、単純に押した事で何かアクションをつけたければ、

if ( jumbleat_sw.read(2) == SW_RELEASED )
{
//  させたい命令を書く
}

 これだけで成立します。

6.まとめ

以上をまとめた「スイッチを押すとカウントする」スケッチです。返しはシリアルモニタに表示させます。

連打してチャタリングの塩梅も確認してみてください。

サンプルスケッチ2

次は複数のスイッチを使ったサンプルを書いてみます。スイッチの「押し」「長押し」「離した」等の状態変化によって振る舞いを変えてみます。

回路図

モーメンタリスイッチとLEDを4個づつ用意。LEDにつける抵抗も必要です。

ピンを設定してB2CSを作動させるまでは、上述通りなので割愛します。

まず、スイッチの状態を一時的な変数に投げ込みます。例えば、ピン2のスイッチであれば、こんな感じです。

byte sw_cur = jumbleat_sw.read(2);

これは.read()は一度呼び出すと、この「読み取り」は(特に「離した」事に対して)リセットされるためです。

そして、返り値の大きい設定からif文で分岐命令します。

離した時の返り値は「SW_RELEASED」「SW_RELEASED_LONG」の2つがありますが、LONGの方が値が大きいので、下記のような書き方でまとめることも出来ます。

if ( sw_cur >= SW_RELEASED )

ということで、離した時「点滅、押した回数のカウントのシリアル表示」、長押し時「ゆっくり点滅」、単に押した時「点灯」という振り分けをしたスケッチを書いてみます。諸々のピン設定や変数は“配列”としてまとめて繰り返し、なるべく簡潔にしています。

各々のスイッチが単独で反応してくれると思います。ここら辺は以前の記事でも書いたやり口と一緒ですが、ライブラリで扱うとスッキリします。

ただ、「離した時」の早い点滅をdelayを使ったfor文でやっていて、このやり方はあまりオススメできません。点滅中は他の事が出来ないので、例えば、同時にスイッチを離しても一個ずつ処理されていくと思います。早い反応を求めないならいいんですけど、並列で処理したい場合は、なるべくforやdelayを使わない「流れる」書き方を工夫する必要があります。

それでも、そうは言ってられない時もあるかもしれません。そんな時は以下の方法もあります。

.count()関数

.read()と違い、こちらは前回呼び出してから現在までの間に、スイッチを押した回数を返す関数です。

例えば、forとdelayを使って、10秒間LEDを点滅をさせるスケッチを書いたとします。.read()だと、その間の状態は1回分しか認識しませんが、.count()であれば、255回までその押した数を教えてくれます。

タクトスイッチ Arduino D2ピンへ
LED&抵抗  Arduino D13ピンへ

ただし、これを使うと、「長押し」も「長押し後の離し」も分かりません。あくまで、更新できない間に押された回数のみです。悪しからず。

チューニング

この“ゲージ判定”はdigitalReadの統計から判別しているので、読みに行く回数が重要になっています。そして、その読み取り回数は、使うArduinoの速さ(クロック周波数)、スケッチの長さなどによって変わってきます。つまり、設定している初期値は、状況によっては弊害にもなるので、自分の状況に合わせて諸々の数値をカスタマイズする必要が出てきます。

ゲージ判定値の調整

「押し」判定しきい値は

.setPushShort( 数値 );

「長押し」判定しきい値は

.setPushLong( 数値 );

で変更できます。

この数値は時間ではなく、読み取り統計のしきい値なので、Arduinoの処理速度(クロック周波数)が高いほど、又はスケッチのループインターバルが早い(スケッチが小さい)ほど、大きい値に。逆であれば小さい値に、といった感じになってくると思います。

初期値では、PushShortを「12」、PushLongを「2000」に設定していて、これはArduinoUno(16MHz)での使用を想定しています。自分の状況に合わせて調整してください。

とはいえ、どんな量か分からないと調整が面倒だと思うので、その時は以下の方法を試してください。

.GetGauge()関数

この関数は実際にカウントされたゲージ量を返します。そこで「サンプルスケッチ1」の回路図で、下記のようなスケッチを実行します。

スイッチを押した時のゲージ量がシリアルモニタ上に表示されます。この値を参考に自分に合ったPushShort、PushLongのしきい値を探してください。

ただし、PushLongは好みで大丈夫ですが、PushShortはあまり低くするとチャタリングも拾うようになってしまい、逆に高すぎると、スイッチ自体の反応が鈍くなってしまうので注意が必要です。

PushShortが1なので、チャタリングにも反応

割り込み頻度の調整

B2CSのタイマー割り込みは初期値で5msに設定しています。でも、これでもやりたい事によっては邪魔になるかもしれません。

.GO()関数は、引数を入れる事で割り込み頻度を変更出来るようにしています。

.GO( ミリ秒 – 整数 );

ただし、間隔を縮めたり伸ばしたりすれば、その分、PushShort、PushLongのしきい値も変わってくるので、スイッチの反応が変わってきます。双方の兼ね合いで調整する必要が出てきます。

最大スイッチ数の変更

.setSW()は最大8個までスイッチを設定できますが、メモリやタスク時間を削減するために減らしたり、或いはもっとたくさんスイッチを扱いたい場合が出てくるかもしれません。

「インスタンスを増やせば」と思うかもしれませんが、技術的に難しかったのでそこの対応は出来ていません。が、ライブラリのファイル内の定数を直接書き換える事で最大数を変更できます。インストールされた「B2CS.h」ファイル内に記述されています。

保存先は環境によって変わるかもしれませんが、Windowsの場合、

C:\Users\ユーザー名\Documents\Arduino\libraries\B2CS

B2CS_PINS_MAXが最大ピン数の初期設定値です。

ちなみにB2CS_PUSH_SHORT、B2CS_PUSH_LONGはゲージ判定しきい値の初期設定なので、もしスケッチの度に.setPushShort()等で変更するのが面倒であれば、ここを書き換えてもらったほうがいいかもしれません。

関数一覧

.setSW(pin_number, pin_setting, pin_logic)

B2CSで使うスイッチ用のピンを設定します。

引数 説明
pin_number byte スイッチを接続したArduinoピン
ピン番号 整数
pin_setting byte スイッチの接続回路
INPUT、INPUT_PULLUP
pin_logic boolean スイッチON時の理論値設定
0 / false / LOW、1 / true / HIGH

.read(pin_number)

ピンの状態を確認します。Byte型での返り値があります。

引数 説明
pin_number byte 確認したいスイッチのピン番号
整数
define定義文字列 数値 状態
– – – – – 0 押されていない
SW_PUSHED 1 スイッチが押されている
SW_PUSHED_LONG 3 長いことスイッチが押されている
SW_RELEASED 5 押した後、スイッチを離した
SW_RELEASED_LONG 7 長いこと押した後スイッチを離した

.count(pin_number)

前回呼び出してから、現在までに押された回数をByte型で返します。

引数 説明
pin_number byte 確認したいスイッチのピン番号
整数

.setPushShort(value)

「押し」判定になるしきい値を変更します。値を大きくするほど、チャタリングしなくなりますが、その分反応が鈍くなり、逆に小さくすると、反応が良くなりますが、チャタリングも拾うようになります。

PushLongより大きい値は入れないでください。

引数 説明
value unsigned Short チャタリングと「押し」を判別するしきい値
整数

.setPushLong(value)

「長押し」判定になるしきい値を変更します。PushShortより小さい値は入れないでください。

引数 説明
value unsigned Short 「長押し」を判別するしきい値
整数

.GO() / .GO(ms)

B2CSのタイマー読み取りを開始します。引数を入れない場合は5msで作動します。

引数 説明
ms byte タイマー割り込みの間隔
ミリ秒(整数)

.B2CS_STOP()

B2CSのタイマー読み取りを停止します。

.GetGauge()

実際に計測しているゲージ量を返します。PushShort以下の場合は前回の値のままになるので、全部の値を確認したければ、PushShortを1に設定するといいかもしれません(チャタリングも出るようになりますが)。

 

まだ試作段階に近いので、どこでどういった不具合が出るのかも分かりません。そういった症状を見つけた方は、コメント欄に書き込んでいただけるとありがたいです。また、使ってみた感想などでも嬉しいです。

参考サイト

【きむ茶工房ガレージハウス】 – 自作ライブラリの作り方

exabugs(Qiita) – はじめての Arduino ライブラリ

白の玉手箱 – Arduino自作ライブラリの作り方

Arduino の PWM と タイマーと tone() 関数

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA