少ないポートでキー入力

 マイコンを使って、ユーザー入力があるアプリを作っていると、ポートが足りないことが時々起こります。 これに対応する方法を考えます。


ポートが足りない時にとるべき措置

 マイコンを使っていると、ポートが足りないという事態によく陥ります。その場合、さまざまな対応方法がありますが、当方でやったことのある方法をご紹介します。

ロジックICでIOを拡張する

 入力/出力がきっちり8ビット足りないとか、8ビットでデータを渡す(=データバス的な)処理がある場合には、ロジックICを使ってIOを拡張しています。条件が合えば、最も安い拡張だと思います。74HC595や、74HC165あたりがターゲットです。

SPIで、IOエキスパンダを接続する

 拡張するIOが、入力出力で数ビットづつだとか、拡張した入力ポートからの信号で割り込み駆動を行う必要があるとか、32ビット以上の大量拡張の必要が生じた場合などは、SPI接続でIOエキスパンダを使用します。MCP23S08が当方の定番です。4個(32ビット)までなら、チップ側のアドレス選択を使って振り分けできるので、チップセレクトを分ける必要もありません。

SPIで、ATTINY2313を接続する

 拡張するIOでPWMを行いたいとか、シリアル回線も必要とかいう需要の場合、SPI接続でマイコンをもうひとつ増設します。当方定番サブマイコンは、アトメルのATTINY2313です。何といっても100円ですし。

ハードとソフトで何とかする

 これが、今回のテーマなのですが、キー入力などの場合でマイコンにADコンバータを搭載している場合、うまく回路を作れば、複数キーを1つのポートで受信できます。

ADコンバータでキー入力

複数キー未対応のADKEY
 ネットで検索してみると、上のような回路図が出てきました。たった1ビットのアナログ入力に7個のキーを割り当てています。ただし、この回路では、複数キーを同時に押した場合、右側の(スイッチ番号が小さいほうの)キーが優先されます。このことが問題にならない場合、非常に有効な方法です。実際、MP3のポータブルプレーヤーや、デジタルフォトフレームなんかの製品にも、この手の回路があるようです。

スイッチ番号 下側抵抗値[Ω] AD電圧[V] 取込値(10bit)
1 680 0.63 129
2 1590 1.26 259
3 2790 1.86 381
4 4790 2.52 517
5 8090 3.16 648
6 14290 3.76 771
7 34290 4.40 901

 マイコン内臓ADCで一般的解像度は10ビットなので、それに合わせて計算してみました。 また、VCCの電圧は5Vとしています。(最近では異論もありましょうが)
 まず、何も押されていなければ、R1によるプルアップで、ADCの電圧はVCCになります。10ビットのADCであれば、上限値である1023が返って来るハズです。  キー1が押されていれば、電圧は0.63Vとなり、取込値は129に、キー2であれば取込値は259になりそうです。もちろん、抵抗の誤差が1%とか、5%とかありますが、この129と259の中点である、194より、小さいかどうかを判定すれば、キー1が押されているということが言えそうです。

//	判定ルーチン1
//	(効率わるい)
int adv2key(int adcval)
	{
	if (adcval < 194)       return 1;
	else if (adcval < 320)  return 2;
	else if (adcval < 449)  return 3;
	else if (adcval < 582)  return 4;
	else if (adcval < 709)  return 5;
	else if (adcval < 836)  return 6;
	else if (adcval < 962)  return 7;

	return 0;
	}

 何も考えずに判定ルーチンを書くと、こんな感じになりました。でもちょっと考えると、このコードは、7回if文を通過するので、効率が悪いと言えます。せっかく不等号で判定するので、いじってみます。

//	判定ルーチン2
//	(ソフト的には効率よさそうだけど・・・)
int adv2key(int adcval)
	{
	if (adcval < 582)	// key 1〜4
		{
		if (adcval < 320)	// key 1 or 2
			{
			if (adcval < 194) return 1;
			else              return 2;
			}
		else				// key 3 or 4
			{
			if (adcval < 449) return 3;
			else              return 4;
			}
		}
	else				// key 5〜7,0
		{
		if (adcval < 836)	// key 5 or 6
			{
			if (adcval < 709) return 5;
			else              return 6;
			}
		else				// key 7 or 0
			{
			if (adcval < 962) return 7;
			else              return 0;
			}
		}

	return 0;	//ここには来ない
	}

 この形だと、どんな場合でも、if文3回通過で判定できるので、効率が良いように見えます。パソコンのプログラムで、あるデータ範囲を判定する方法であれば、このように、まず大きく2つに分けて、その中をさらに2つに・・・というアプローチは、効率的です。ところが、マイコンのキー判定ルーチンとしては、非効率なのです。
 マイコンシステムにキーが7個あるとして、マイコン動作時間の全てのうち、キーが押されている時間は、僅かなものであると言えましょう。つまり、ADコンバータから取得されるデータは圧倒的に1023近辺の値に偏っていることになります。そこで、この部分を先に判定することにより、キーが押されていないケースの場合、if文一回で戻れるように変更しました。

//	判定ルーチン3
//	(この場合、これがいいと思う)
int adv2key(int adcval)
	{
	if (adcval >= 962) return 0;
	if (adcval < 582)	// key 1〜4
		{
		if (adcval < 320)	// key 1 or 2
			{
			if (adcval < 194) return 1;
			else              return 2;
			}
		else				// key 3 or 4
			{
			if (adcval < 449) return 3;
			else              return 4;
			}
		}
	else				// key 5〜7,0
		{
		if (adcval < 836)	// key 5 or 6
			{
			if (adcval < 709) return 5;
			else              return 6;
			}
		else				// key7
			{
			return 7;
			}
		}

	return 0;	// ここにはこない
	}

無作為研究所トップページに戻る

copyright(c)2007 by MUSAKUI-LABO