これは「PICによる赤外線通信」の続きです。「PICによる赤外線通信8」で、赤外線受信処理部分のプログラムはできてしまったので、タイトルを変更しました。
残りの作業について
「PICによる赤外線通信5 受信プログラムの周りの機能について」で、4つの必要な機能について説明しています。そのうちの1つ「赤外線受信機能」部分のプログラムは作成できたので、残る作業は次の3つです。
- 戦車制御機能
- 受信と制御の連絡機能
- 停止機能
出力端子について
「戦車制御機能」部分のプログラムを作る前に、まず出力端子について決定することがあります。
「PICによる赤外線通信5 全体的な回路」で説明しているように、赤外線リモコン戦車は2つのモーターと1つのLEDを制御する必要があります。そして、そのためにPICの端子のうち5つを出力端子として使います。
出力端子としてどの端子を使うのかはまだ決めていませんでしたが、ここで5つの出力端子はすべてLATC(PORTC)を使う事にしました。
LATCのそれぞれのビットの振り分けは次の通りです。
ビット番号 | 説明 |
0 | 右モーター用のL9110のIA端子を制御する |
1 | 右モーター用のL9110のIB端子を制御する |
2 | 左モーター用のL9110のIA端子を制御する |
3 | 左モーター用のL9110のIB端子を制御する |
4 | LEDを制御する |
出力を制御するためのプログラム
まず出力(LATC)を制御するためのプログラムを作っていきましょう。
右モーターを制御するには、LATCの0と1のビットを操作すればよいので、
// 右モーター停止
LATC0 = false;
LATC1 = false;
// 右モーター正転
LATC0 = true;
LATC1 = false;
// 右モーター逆転
LATC0 = false;
LATC1 = true;
のようにすればよいですが、これだと逆転から正転に切り替えるときには瞬間的に2つのビットがtrueになり、少し気持ち悪いです。また、左モーターの制御も考えると4つのビットは同時に変更したくなります。ただし、LEDを制御するビットは変更してはいけません。
これらを考慮した結果、LATCの値を変更する前に、いったん変数にセットし、LATCには変数をコピーするという設計にしました。
// LATCに出力する値を保持したり、決定したりするときに使うデータ型
typedef union
{
// ビット単位でアクセスするための定義
struct
{
uint8_t RMTR_FOR : 1; // 右モーターを正転する時に立てるビット
uint8_t RMTR_BACK : 1; // 右モーターを逆転する時に立てるビット
uint8_t LMTR_FOR : 1; // 左モーターを正転する時に立てるビット
uint8_t LMTR_BACK : 1; // 左モーターを逆転する時に立てるビット
uint8_t LED : 1; // LEDを点灯する時に立てるビット
};
// モーター制御の4bitだけをアクセスするための定義
struct
{
uint8_t MOTOR : 4;
};
// uint8_t としてアクセスするための定義
uint8_t UINT8;
} OUT_DATA_t;
// LATCに出力するモーター制御値を保持しておくバッファ
static OUT_DATA_t OUT_DATA = {.UINT8 = 0};
モーターの制御値を出力する
赤外線通信で送信されるデータのうち4ビットのデータが戦車の移動を指示しています。詳細は、「PICによる赤外線通信5 受信データ」で説明しています。
この4ビットのデータはモーター制御のためLATCに出力する4ビットのデータとは一対一には対応していません。例えば、受信データの4ビットが2(2進数で0010)のときは「高速で前進」を表します。しかし0010をそのままLATCの3~0ビットにセットしても戦車は前進しません。(左モーター停止・右モーター逆転になってしまいます。)
つまり受信データの4ビット値をLATCに出力する4ビット値に変換するするための処理が必要になります。
またモーターの制御はソフトウェアPWMで行います。その理由は「PICによる赤外線通信5 モーターの制御について」で説明しています。
つまり大まかな設計として、戦車を動かすために次の2種類の処理が必要です。
- 赤外線受信処理で受け取った4ビットのデータから左右のモーターの回転方向や回転速度に変換する処理
- 左右のモーターの回転方向と回転速度に応じて、ソフトウェアPWMでモーターを制御する処理
これらの処理を行うプログラムは別途作成するとして、とりあえずはLATCに4ビットデータを出力するための関数を用意します。
/**
* モーターを制御するためにLATCに出力する値を変更する。
*/
void OUT_SetMotor(uint8_t motor_control)
{
OUT_DATA.MOTOR = motor_control;
LATC = OUT_DATA.UINT8;
}
引数のmotor_controlにはプログラムの別の場所(前述の2つの処理)で計算されたモーターを制御する4ビットの値が入っています。その値でOUT_DATA.MOTORを更新した後、LATCにセットしています。
この関数をコールするように、前述の2つの処理を今後作っていきます。
LEDの制御値を出力する
赤外線通信で送信されるデータのうち1ビットのデータがLEDのOn/Offを指示しています。
モーター制御と異なり、LEDの制御としては点灯する/しないのBool値なので、下のような関数を用意します。
/**
* LEDを制御するために、LATCに出力する値を変更する。
*/
void OUT_SetLed(bool switch_on)
{
OUT_DATA.LED = false;
if ( switch_on ) OUT_DATA.LED = true;
LATC = OUT_DATA.UINT8;
}
引数のswitch_onに応じて、OUT_DATA.LEDのtrue/falseを変更したあと、LATCにセットしています。
そして、赤外線受信処理でデータ受信が完了した後、この関数をコールするようにします。
XC8は構造体ビットフィールの取り扱いを苦手としています。
OUT_DATA.LED = switch_on;
と1行で書いた方がCソースとしては簡潔で綺麗なのですが、アセンブラリストが長くなってしまうので、上のようなコードにしています。
コメント