タイプBのリモコンを使った動作仕様の一つ目を、いくつかの方法で作ってみます。
(3) タイプBのリモコンをマイコン・カーに接続して動かすプログラムを作成
前回のタイプAと同様にして、「パラレル式リモコンタイプB処理」 を作ることにします。
前進スイッチ(SW1) がオンの間、モータ右+ と モータ左+ のみオンにする。
右折スイッチ(SW2) がオンの間、モータ左+ のみオンにする。
左折スイッチ(SW3) がオンの間、モータ右+ のみオンにする。
後退スイッチ(SW4) がオンの間、モータ右- と モータ左- のみオンにする。
これらは同時には動かせず、どれか一つだけ動作可能。 優先順位は規定しない。
停止スイッチ(SW5) は使わない。
右折スイッチで左の車輪だけ回る、というのがちょっと面白いですね(逆も同様)。 タイプBは、タイプAと比べて片手で操作しやすいように考えています。 それでは、parallel_01 と parallel_02 を基にしてプログラムを作ってみましょう。 初期設定の部分には変更がありません。 スイッチ・オンのとき 0 、スイッチ・オフのとき 1 というのもプログラム中では読みにくいので #define(デファイン: 文字列の置き換え) でマクロ定義しておきました。
#include <hidef.h>
#include "derivative.h"#define PORT_SW1 ( PTB_PTB0 )
#define PORT_SW2 ( PTB_PTB1 )
#define PORT_SW3 ( PTB_PTB2 )
#define PORT_SW4 ( PTB_PTB3 )
#define PORT_MT_RP ( PTB_PTB6 )
#define PORT_MT_RM ( PTB_PTB7 )
#define PORT_MT_LP ( PTB_PTB4 )
#define PORT_MT_LM ( PTB_PTB5 )#define SW_ON ( 0 )
#define SW_OFF ( 1 )void main( void ){
CONFIG1 = 0x11; // LVI無効、COP無効
PTAPUE = 0x24;
PTA = 0x08;
DDRA = 0x0A;
PTBPUE = 0x0F;
PTB = 0x00;
DDRB = 0xF0;
for( ; ; ){
if ( PORT_SW1 == SW_ON ){
PORT_MT_RP = 1; // モータ右+
PORT_MT_LP = 1; // モータ左+
} else {
PORT_MT_RP = 0;
PORT_MT_LP = 0;
}
if ( PORT_SW2 == SW_ON ){
PORT_MT_LP = 1; // モータ左+
} else {
PORT_MT_LP = 0;
}
if ( PORT_SW3 == SW_ON ){
PORT_MT_RP = 1; // モータ右+
} else {
PORT_MT_RP = 0;
}
if ( PORT_SW4 == SW_ON ){
PORT_MT_RM = 1; // モータ右-
PORT_MT_LM = 1; // モータ左-
} else {
PORT_MT_RM = 0;
PORT_MT_LM = 0;
}
}
}
一見すると仕様通りに書けていて、これで良いようにも見えます。 しかし、右+ または 左+ の出力をオンしてもすぐにオフされてしまう作りになっていることがわかるでしょう。
これではダメなのですね。 おや? もしかしたら、正しく動かすのは難しいのかも? この後で正解例を示しますので、チャレンジ精神を発揮して、自力でプログラムを作ってみてください。 マイコンに書き込んで、動かして確かめてもよいです。
(ここでしばらく自習タイム .。o O)
いかがでしょう、動作するプログラムができましたか? それでは正解例を見てみましょう。
for( ; ; ){
if ( PORT_SW1 == SW_ON ){
PORT_MT_RP = 1; // モータ右+
PORT_MT_RM = 0; // モータ右-
PORT_MT_LP = 1; // モータ左+
PORT_MT_LM = 0; // モータ左-
} else {
if ( PORT_SW2 == SW_ON ){
PORT_MT_RP = 0;
PORT_MT_RM = 0;
PORT_MT_LP = 1;
PORT_MT_LM = 0;
} else {
if ( PORT_SW3 == SW_ON ){
PORT_MT_RP = 1;
PORT_MT_RM = 0;
PORT_MT_LP = 0;
PORT_MT_LM = 0;
} else {
if ( PORT_SW4 == SW_ON ){
PORT_MT_RP = 0;
PORT_MT_RM = 1;
PORT_MT_LP = 0;
PORT_MT_LM = 1;
} else { // 全SWオフ
PORT_MT_RP = 0;
PORT_MT_RM = 0;
PORT_MT_LP = 0;
PORT_MT_LM = 0;
}
}
}
}
}
ポート出力の 4行をまとめて複数個所に記述し、毎回どれか一か所だけを通るような作りにしています。 これなら間違いないですね。 しかし、右へ右へインデントが深くなっていくのが好きじゃない、という場合は次のように書くこともできます。
for( ; ; ){
if ( PORT_SW1 == SW_ON ){
PORT_MT_RP = 1; // モータ右+
PORT_MT_RM = 0; // モータ右-
PORT_MT_LP = 1; // モータ左+
PORT_MT_LM = 0; // モータ左-
} else if ( PORT_SW2 == SW_ON ){
PORT_MT_RP = 0;
PORT_MT_RM = 0;
PORT_MT_LP = 1;
PORT_MT_LM = 0;
} else if ( PORT_SW3 == SW_ON ){
PORT_MT_RP = 1;
PORT_MT_RM = 0;
PORT_MT_LP = 0;
PORT_MT_LM = 0;
} else if ( PORT_SW4 == SW_ON ){
PORT_MT_RP = 0;
PORT_MT_RM = 1;
PORT_MT_LP = 0;
PORT_MT_LM = 1;
} else { // 全SWオフ
PORT_MT_RP = 0;
PORT_MT_RM = 0;
PORT_MT_LP = 0;
PORT_MT_LM = 0;
}
}
なんとなくカッコいいようにも見えます。 プロジェクトごと圧縮したものを parallel_03.zip に置いておきます。
もっとほかの書き方もできます。 どうせいつも 4本全部出力するのなら、まとめてしまうのも良さそうですね。 全文を示しましょう。
#include <hidef.h>
#include "derivative.h"#define PORT_SW1 ( PTB_PTB0 )
#define PORT_SW2 ( PTB_PTB1 )
#define PORT_SW3 ( PTB_PTB2 )
#define PORT_SW4 ( PTB_PTB3 )
#define PORT_MT ( PTB )#define SW_ON ( 0 )
#define SW_OFF ( 1 )
#define MTR_OUT_FWD ( 0x50 )
#define MTR_OUT_TNR ( 0x10 )
#define MTR_OUT_TNL ( 0x40 )
#define MTR_OUT_BAK ( 0xA0 )
#define MTR_OUT_STP ( 0x00 )void main( void ){
CONFIG1 = 0x11; // LVI無効、COP無効
PTAPUE = 0x24;
PTA = 0x08;
DDRA = 0x0A;
PTBPUE = 0x0F;
PTB = 0x00;
DDRB = 0xF0;
for( ; ; ){
if ( PORT_SW1 == SW_ON ){
PORT_MT = MTR_OUT_FWD;
} else if ( PORT_SW2 == SW_ON ){
PORT_MT = MTR_OUT_TNR;
} else if ( PORT_SW3 == SW_ON ){
PORT_MT = MTR_OUT_TNL;
} else if ( PORT_SW4 == SW_ON ){
PORT_MT = MTR_OUT_BAK;
} else { // 全SWオフ
PORT_MT = MTR_OUT_STP;
}
}
}
for の中がずいぶんすっきりして見えるようになりました。 parallel_04.zip に置いておきます。
この書き方のデメリットは、もし後でポートの変更があったときに対応しにくい、という点です。 たとえば、PTB の下位 4ビットのいずれかが出力ポートとしてほかの用途に使われるようになったとき、困ってしまいますね。 いろいろな書き方にはそれぞれ一長一短があるということです。
ところで、「ずっと if 文で書いているけど switch 文はどうだろう?」 ということも気になりますね。 こんな書き方もできるんです。
#include <hidef.h>
#include "derivative.h"#define PORT_SW ( PTB )
#define PORT_MT ( PTB )#define SW_PTN_FWD ( 0x0E )
#define SW_PTN_TNR ( 0x0D )
#define SW_PTN_TNL ( 0x0B )
#define SW_PTN_BAK ( 0x07 )
#define MTR_OUT_FWD ( 0x50 )
#define MTR_OUT_TNR ( 0x10 )
#define MTR_OUT_TNL ( 0x40 )
#define MTR_OUT_BAK ( 0xA0 )
#define MTR_OUT_STP ( 0x00 )void main( void ){
CONFIG1 = 0x11; // LVI無効、COP無効
PTAPUE = 0x24;
PTA = 0x08;
DDRA = 0x0A;
PTBPUE = 0x0F;
PTB = 0x00;
DDRB = 0xF0;
for( ; ; ){
switch ( PORT_SW & 0x0F ){
case SW_PTN_FWD:
PORT_MT = MTR_OUT_FWD;
break;
case SW_PTN_TNR:
PORT_MT = MTR_OUT_TNR;
break;
case SW_PTN_TNL:
PORT_MT = MTR_OUT_TNL;
break;
case SW_PTN_BAK:
PORT_MT = MTR_OUT_BAK;
break;
default:
PORT_MT = MTR_OUT_STP;
}
}
}
これも実行コードは少なく見えてちょっと良さそうなのですが、入力ポートに変更があったとき対応しにくい、という心配があります。
動きは先ほどまでとほとんど同じですが、実は違うところもあります。 スイッチを複数個押したとき、先ほどまでのプログラムでは 『前進』 の優先度が高く 『後退』 が低かったのです。 今度のプログラムでは、複数個押した場合は用意されたパターンに合わないので停止となります。 parallel_05.zip として置いておきます。
いかがだったでしょうか。 パラレル式リモコンのタイプAとタイプBで、気にしなくてはならないポイントが結構違っていましたね。 順番に読んでいけばすべて理解できるはずですから、あせらずに楽しみながら学習を続けましょう。
最後にひとつ宿題です。 Aさんは、上の parallel_03 を少し変更して次のようなプログラムを考えました。 毎回 4本の出力ポートを全部叩くのではなく、オンするところだけに絞った方法です。 オフにするのが抜けているということはなく、スイッチを離したときに全スイッチ・オフのところで確実に出力オフできると考えたのです。 さて、これはうまく動くでしょうか? もしまずいとすれば、どのように動くのでしょうか?
for( ; ; ){
if ( PORT_SW1 == SW_ON ){
PORT_MT_RP = 1;
PORT_MT_LP = 1;
} else if ( PORT_SW2 == SW_ON ){
PORT_MT_LP = 1;
} else if ( PORT_SW3 == SW_ON ){
PORT_MT_RP = 1;
} else if ( PORT_SW4 == SW_ON ){
PORT_MT_RM = 1;
PORT_MT_LM = 1;
} else { // 全SWオフ
PORT_MT_RP = 0;
PORT_MT_RM = 0;
PORT_MT_LP = 0;
PORT_MT_LM = 0;
}
}
次回は、タイプBのリモコンを使ったもう一つの動作仕様を作ってみます。
『参考文献』
「試しながら学ぶHC08マイコン入門」 (CQ出版)
第9章 USB-HC08デバッグ・ツールを自作する
Appendix F KMC908QY4Aユーザ・モード・モニタ入りマイコンの知識
筆者のホームページ 『マイコン工作の実験室』
組み込みエンジニア KAWANO
