こんにちは、ナナです。
ビュートローバーには左右2つのモーターが付いています。これらを制御することで走ることができます。前進、後退、回転など本章では基本的なモーターの回し方を学びましょう。
本記事では次の疑問点を解消する内容となっています。
では、モーター制御の仕組みを学んでいきましょう。
ビュートローバーのDCモーターについて
まずはDCモーターが何なのかを学びましょう。
DCモーター
ビュートローバーに搭載されているモーターはDCモーターと呼ばれるものです。DCとはDirect Current(直流)という意味であり、直流電流という種類の電流で回るモーターです。
モーターの区別と進行方向の定義
2つのモーターの区別と進行方向をはっきりさせておきましょう。電池BOXがある方を前進方向と定義します。

前進方向を基準にモーターを右/左として区別しましょう。
モーター駆動のための電池と動かす時の注意
ここまでLEDを点灯したり音を鳴らしたりしてビュートローバーを動かしてきました。その際にパソコンとUSBケーブルが接続されている状態であれば電池がなくても動いていたことでしょう。
しかし、モーターに関しては電池が電源となっているため、電池を付けて電池用の金属スイッチをONにしないと回せません。
また、モーターを動かすとビュートローバーが走り出しますので、机の上などに置きながら開発作業を行っている方は落下させないように注意してください。念のためプログラムを書き込むときはひっくり返しておくと安全です。

モーターの回転方向を切り替える方法
DCモーターを回転させるには
DCモーターを回転させること自体は簡単です。電池は直流電流を流すため電池をつなぐことでDCモーターは回ります。

モーターには極性があるため、モーターを反対に回転させたければ電池を反転してつなげば逆方向に回ることになります。

回転方向の切り替え
ビュートローバーで走り回りたいのですから、前進だけでなく後退もしたいですよね。しかし、わざわざ電池の向きを入れ替えていたら大変です。
そこでモーターの回転方向を制御できる回路が必要になるわけです。ここで出てくるのがHブリッジ回路と呼ばれるDCモーター制御用の回路です。
ビュートローバーの回路図を見ると右側に大きな回路が書かれています。これがモーターのHブリッジ回路です。モーターが2つあるので片方だけ抜き出してみましょう。

簡略化しましたが、これでもかなり複雑な回路です。
オレンジ色で囲った素子がFET(電界効果トランジスタ)と呼ばれるものです。Hブリッジ回路では4つのトランジスタを使ってモーター回転を制御します。
FETトランジスタにはN型とP型の2つの種類があります。このHブリッジ回路はそれぞれ2つのトランジスタが使われているのがわかります。
トランジスタの役割
電子回路でトランジスタという回路はよく出てきます。トランジスタの主な役割はスイッチングです。
スイッチといえばSW制御の章で登場したプッシュスイッチなどが思い浮かぶでしょう。トランジスタもスイッチと同じ役割があるのですが、使われ方が違います。

プッシュスイッチが人が押すスイッチならば、トランジスタはマイコンにより押すスイッチです。
Hブリッジによる電流方向の切り替え
トランジスタとはスイッチングするための素子であるという視点で、再度Hブリッジ回路を見てみます。トランジスタ部分をわかりやすくプッシュスイッチ回路に変えて簡略化してみましょう。

この図は実際の回路とはもちろん異なりますが、モーター制御の考え方を知るためものです。モーターを正回転/逆回転させるためにはモーターに流れる電流を逆にする必要がありました。
このトランジスタという4つのスイッチを適宜切り替えることでそれが可能になります。

このように斜めのラインのSWをONにすることでモーターへ流れる電流方向を切り替えることができるのがわかります。この①~④のトランジスタというスイッチを皆さんのプログラムからマイコンに押させることができればモーターの回転方向を制御できることになるのです。
FETのスイッチを入れるためには
FETはスイッチであるということですが、マイコンからこのスイッチを電気的に押す必要があるわけです。ではどのようにスイッチを押すのかというとFETにつながるポート電圧を上げ下げすることでスイッチのON/OFFを切り替えることができます。
FETには3つの端子が付いており、ゲート(G)、ソース(S)、ドレイン(D)と呼ばれます。ゲートをマイコンのポートに接続しスイッチング制御を行います。
N型FETのゲート電圧によるスイッチ状態

N型のFETの場合はゲートの電圧をHighにすることでスイッチがONになります。ONになるとドレインからソースに向けて電流が流れこみます。
P型FETのゲート電圧によるスイッチ状態

P型のFETの場合はゲート電圧をLowにすることでスイッチがONになります。ONになるとソースからドレインに向けて電流が流れ込みます。
スイッチを切り替えるためのI/Oポート制御
トランジスタというスイッチを切り替えるためにはI/Oポートを利用します。右モーターの場合はP30とP31により回転方向を切り替えることができます。

I/Oポートの出力電圧とモーターの回転方向は次の通りです。左モーターも載せておきましょう。

P30とP31が同一値の場合は上もしくは下のスイッチが有効になり、モーターに電流が流れなくなります。それにより回転が停止します。

PWMを使ったモーターの回転速度制御
モーターの回転速度はどうやって決まるのか
モーターには回転方向以外に回転速度という要素もあります。
モーターの回転速度は流れる電流量に影響されます。それは水車において川の流れの量が大きいと速く回り、少ないとゆっくり回るのをイメージするとわかりやすいでしょう。

しかし、直流電源は基本的に流す/流さないのどちらかの制御となるため、モーターも回るか/回らないかという2択になってしまいます。単純に考えれば最大の速さで回すか、止めるかしかできません。
これでは不便ですね。なんとかして速さを変えられないかと考えるわけです。
発想を変えてみましょう。流す/流さないは少なくとも制御できるわけですから、流す→流さない→流す→流さない→・・・を高速で切り替えれば水量を調整できると思いませんか。
ON→OFF→ON→OFF→・・・こんな制御がしたい!
ここで再び登場するのがPWM機能です。スピーカーの音を出力するときに使ったPWM機能がここでも使われることになります。
PWM制御によるモーター回転速度の変化
モーターというのは惰性で動きます。そのため電流を流し始めると徐々に回転し始め、電流を止めると徐々に回転が止まっていきます。

では高速でモーター電源をON⇔OFFしてみるとどうなるでしょう。

皆さんの家庭ある扇風機の電源を高速で ON ⇔ OFF を繰り返してみるとより実感できるでしょう。少しゆっくりした速度で回っているのがわかると思います。
モーターとPWMのデューティ比
PWM機能を利用することでモーターの回転速度を調整することができます。どの程度の速度にするかはデューティ比で調整することになります。
デューティ比とは周期に対するON波形幅の割合いのことでしたね。スピーカーで音を出力する時は50%にしていましたが、モーターの場合は目的の回転速度に従いデューティ比を変化させます。
モーターの回転速度を制御するタイマZのPWM機能の使い方
タイマZはデータシート「12章 タイマZ」に記載されています。全60ページ程度でありH8/36064で搭載しているタイマ機能の中で最も高機能で複雑なタイマです。
これくらいのページ数になると全てを読み込むのが困難になります。今回の読むべきポイントはPWMですから、データシートのレジスタに関する説明とPWMに関わる説明を突き合わせながら読み解いていきます。
「12.1章 特長」「12.3章 レジスタの説明」「12.4.5章 PWM モード」を中心に参照しましょう。
特徴
- 最大8種類の入出力処理が可能
- 合計8本のジェネラルレジスタ(GR)を持ち、独立してアウトプットコンペア/インプットキャプチャの機能設定が可能
- カウンタ入力クロック:5種類
- 動作モード:アウトプットコンペア/インプットキャプチャ/同期動作/PWMモード/リセット同期PWMモード/相補PWMモード/バッファ動作
- 内部16ビットバスによる高速アクセス
- タイマ出力初期値を任意に設定可能
- 外部トリガによるタイマ出力禁止機能
- 割り込み要因:11種類
特徴の解説をします。
独立してアウトプットコンペアの機能設定が可能
今回は2つのモーターをPWMを使って制御することになります。8本のジェネラルレジスタのうちの2つ(GRB0とGRC0)を独立して制御します。
動作モード:PWMモード
動作モードにはリセット同期PWMモードや相補PWMモードといったPWM機能もありますが、今回は普通のPWMモードを利用します。
16ビットバス
今回のタイマZは2Byte管理のカウンタを管理しています。タイマB1やタイマVと比べるとカウント範囲が大きくなっているため、より多くの時間をカウントすることができます。
レジスタ構成
複数チャネルに対する共通用レジスタ
- タイマスタートレジスタ(TSTR)
- タイマモードレジスタ(TMDR)
- タイマPWMモードレジスタ(TPMR)
- タイマファンクションコントロールレジスタ(TFCR)
- タイマアウトプットマスタイネーブルレジスタ(TOER)
- タイマアウトプットコントロールレジスタ(TOCR)
チャネル0専用のレジスタ
- タイマコントロールレジスタ_0(TCR_0)
- タイマI/OコントロールレジスタA_0(TIORA_0)
- タイマI/OコントロールレジスタC_0(TIORC_0)
- タイマステータスレジスタ_0(TSR_0)
- タイマインタラプトイネーブルレジスタ_0(TIER_0)
- PWMモードアウトプットレベルコントロールレジスタ_0(POCR_0)
- タイマカウンタ_0(TCNT_0)
- ジェネラルレジスタ(GRA_0、GRB_0、GRC_0、GRD_0)
非常に多くのレジスタがあり、制御がかなり難しそうですね。このレベルのハードウェア制御をした経験があれば、大概のレジスタ制御はなんとかできる技術力が身についているでしょう。
太字のレジスタが今回PWMで使用するレジスタです。これらの洗い出しはデータシートを読み込んで調べるしかありません。
タイマスタートレジスタ(TSTR)とは
タイマクロックのカウントを開始/停止するための重要なレジスタです。構造はシンプルですね。今回はチャネル0側を使うことでモーター制御を行います。
タイマPWMモードレジスタ(TPMR)とは
ピンの端子をPWMで使用するかを設定するレジスタです。今回のPWMはFTIOB0とFTIOC0につながっていますのでPWMB0とPWMC0のビットを1とする必要があります。
タイマアウトプットマスタイネーブルレジスタ(TOER)とは
FTIOの端子から外部に対して電圧波形を出力するかどうかを設定するレジスタです。今回はPWMを利用してFTIOB0とFTIOC0から外部に出力する必要がありますので、EB0とEC0には0を設定する必要があります。
タイマアウトプットコントロールレジスタ(TOCR)、PWMモードアウトプットレベルコントロールレジスタ_0(POCR_0)とは
この2つのレジスタは連携してPWM出力波形の電圧切り替えのパターンを決定します。「12.4.5章 PWM モード」の表12.3に組み合わせが記載されています。
PWMモードアウトプットレベルコントロールレジスタが重要な役割があり、この設定値が0か1かによって出力する波形が逆になります。

モーター制御の場合はデューティ比を変化させることで回転速度を変化させます。自分がどちらの波形を出そうとしているかによってGRBやGRCレジスタに設定するデューティー比の割合が変化しますので意識して選択する必要があります。
タイマコントロールレジスタ_0(TCR_0)
分周設定とコンペアマッチによるクリア条件を設定する大事なレジスタです。クリア条件はGRAとの一致判定でクリアしたいためCCLR0~CCLR2には1を設定します。
タイマカウンタ_0(TCNT_0)
タイマZにおけるタイマクロックのカウンタ管理を行うレジスタです。2Byteの大きさのため0~65535までカウントアップすることが可能です。このレジスタは書き込みも可能なため好きなタイミングで値をクリアしたり変更することができます。
ジェネラルレジスタ(GRA_0、GRB_0、GRC_0、GRD_0)
PWMで波形のON/OFFタイミングを設定するレジスタです。GRAは波形周期を設定し、GRBとGRCは左右のモーターのデューティ比によって設定する値を変化させます。
これらのレジスタを制御することでモーター回転速度を調整することができます。
PWMとI/Oポートでモーターを制御するMotorモジュールの作成
モーター制御の方法がわかりましたのでプログラムの作成を行います。いつもの手順でmotor.cとmotor.hをプロジェクトに登録してください。
Motorモジュールの役割検討
モーターモジュールでは次の機能を実装したいと思います。
- モーターは左右2つがある
- 回転方向として前進、後退がある
- 初期状態としてモーターは停止状態とする
- 走行方法として前進、後退、左回転、右回転、停止をインターフェースとしてサポートする。
- 速度調整ができるインターフェースとする。
では、これらを満たすインターフェース仕様を検討します。
Motorモジュールのインターフェース仕様書
1.提供ヘッダファイル名
#include “motor.h”
2.定数定義

3.インターフェース定義
3.1 初期化

3.2 走行開始

3.3 走行停止

課題:Motorモジュールを使ってモーターを回してみよう
課題1
課題内容
インターフェース仕様に従いモーターモジュールを作成せよ。motor.cとmotor.hは次のプログラムをベースにして修正を加えよ。
motor.h
#ifndef MOTOR_H
#define MOTOR_H
//------------------------------------------------
//------------------------------------------------
// マクロ定義(Macro definition)
//------------------------------------------------
//------------------------------------------------
// 型定義(Type definition)
//------------------------------------------------
//------------------------------------------------
// プロトタイプ宣言(Prototype declaration)
//------------------------------------------------
//------------------------------------------------
#endif // MOTOR_H
motor.hに追記すべきこと
- 列挙型enumを使用し走行方向の定数を定義せよ。
- インターフェースのプロトタイプ宣言を追加せよ。
motor.c
#include "iodefine.h"
#include "motor.h"
//------------------------------------------------
// 概 要:初期化
// 引 数:なし
// 戻り値:なし
//------------------------------------------------
void Motor_init(void)
{
}
//------------------------------------------------
// 概 要:走行開始
// 引 数:dir 方向 E_MOTOR_FORWARD 前方
// E_MOTOR_BACKARD 後方
// E_MOTOR_TURN_RIGHT 右回転
// E_MOTOR_TURN_LEFT 左回転
// duty デューティ比(0 - 99)
// 戻り値:0 正常
// -1 異常
//-------------------------------------------------
int Motor_runStart(E_MOTOR_DIR dir, unsigned char duty)
{
}
//------------------------------------------------
// 概 要:走行停止
// 引 数:なし
// 戻り値:0 正常
// -1 異常
//------------------------------------------------
int Motor_runStop(void)
{
}
motor.cに追記すべきこと
- Motor_init関数ではPWM機能を動かすのに必要な処理を実施。ただしタイマカウントはまだ開始しない。 データシートの「図12.21 PWM モードの設定手順例」を参考にして値を設定する。
- Motor_runStart関数では与えられた引数の走行種別に従い、左右のモーターのI/Oポートを適切に切り替える。また、指定されたデューティ比に応じてGRB0とGRC0の値を調整し回転速度を変化させる。
- Motor_runStop関数では左右のモーターのI/Oポートを停止状態にし、タイマカウントを停止する。
motor.h
#ifndef MOTOR_H
#define MOTOR_H
//------------------------------------------------
//------------------------------------------------
// マクロ定義(Macro definition)
//------------------------------------------------
//------------------------------------------------
// 型定義(Type definition)
//------------------------------------------------
typedef enum
{
E_MOTOR_FORWARD = 0, // 前方
E_MOTOR_BACKARD, // 後方
E_MOTOR_TURN_RIGHT, // 右回転
E_MOTOR_TURN_LEFT, // 左回転
} E_MOTOR_DIR;
//------------------------------------------------
// プロトタイプ宣言(Prototype declaration)
//------------------------------------------------
void Motor_init(void);
int Motor_runStart(E_MOTOR_DIR dir, unsigned char duty);
int Motor_runStop(void);
//------------------------------------------------
#endif // MOTOR_H
- 走行方向の列挙型定数を定義する。
- インターフェース仕様書に従ったプロトタイプ宣言を記述する。
motor.c
#include "iodefine.h"
#include "motor.h"
//------------------------------------------------
// 概 要:初期化
// 引 数:なし
// 戻り値:なし
//------------------------------------------------
void Motor_init(void)
{
TZ.TSTR.BIT.STR0 = 0; // タイマZのch0をカウント停止
TZ0.TCR.BIT.TPSC = 0; // 分周なし
TZ0.TCR.BIT.CKEG = 0; // 立上りエッジカウント
TZ0.TCR.BIT.CCLR = 1; // GRAでコンペアマッチクリア
TZ.TPMR.BIT.PWMB0 = 1; // 右モーターPWMモード
TZ.TPMR.BIT.PWMC0 = 1; // 左モーターPWMモード
// HighからLowへ切り替わる波形
TZ.TOCR.BIT.TOB0 = 0; // PWM波形はHigh
TZ.TOCR.BIT.TOC0 = 0; // PWM波形はHigh
TZ0.POCR.BIT.POLB = 0; // ローアクティブ
TZ0.POCR.BIT.POLC = 0; // ローアクティブ
TZ.TOER.BIT.EB0 = 0; // FTIOB0は出力
TZ.TOER.BIT.EC0 = 0; // FTIOC0は出力
TZ0.TCNT = 0; // カウンタクリア
TZ0.GRA = 12000; // 1ms周期のPWMとして動作させる
TZ0.GRB = 0; // 仮設定
TZ0.GRC = 0; // 仮設定
}
//------------------------------------------------
// 概 要:走行開始
// 引 数:dir 方向 E_MOTOR_FORWARD 前方
// E_MOTOR_BACKARD 後方
// E_MOTOR_TURN_RIGHT 右回転
// E_MOTOR_TURN_LEFT 左回転
// duty デューティ比(0 - 99)
// 戻り値:0 正常
// -1 異常
//------------------------------------------------
int Motor_runStart(E_MOTOR_DIR dir, unsigned char duty)
{
// デューティ比100%以上は異常扱い
if (duty >= 100)
{
return -1;
}
// 走行方向に従ったポート切り替え
if (dir == E_MOTOR_FORWARD)
{
// 右モーター前進
IO.PDR3.BIT.B0 = 1;
IO.PDR3.BIT.B1 = 0;
// 左モーター前進
IO.PDR3.BIT.B2 = 1;
IO.PDR3.BIT.B3 = 0;
}
else if (dir == E_MOTOR_FORWARD)
{
// 右モーター後退
IO.PDR3.BIT.B0 = 0;
IO.PDR3.BIT.B1 = 1;
// 左モーター後退
IO.PDR3.BIT.B2 = 0;
IO.PDR3.BIT.B3 = 1;
}
else if (dir == E_MOTOR_TURN_RIGHT)
{
// 右モーター後退
IO.PDR3.BIT.B0 = 0;
IO.PDR3.BIT.B1 = 1;
// 左モーター前進
IO.PDR3.BIT.B2 = 1;
IO.PDR3.BIT.B3 = 0;
}
else if (dir == E_MOTOR_TURN_LEFT)
{
// 右モーター前進
IO.PDR3.BIT.B0 = 1;
IO.PDR3.BIT.B1 = 0;
// 左モーター後退
IO.PDR3.BIT.B2 = 0;
IO.PDR3.BIT.B3 = 1;
}
else
{
return -1;
}
// GRAの周期レジスタをベースにデューティ比の割合を設定
TZ0.GRB = TZ0.GRA * (duty / 100.0);
TZ0.GRC = TZ0.GRA * (duty / 100.0);
// タイマZのch0をカウント開始してPWM動作開始
TZ.TSTR.BIT.STR0 = 1;
return 0;
}
//------------------------------------------------
// 概 要:走行停止
// 引 数:なし
// 戻り値:0 正常
// -1 異常
//------------------------------------------------
int Motor_runStop(void)
{
// 右モーター停止
IO.PDR3.BIT.B0 = 1;
IO.PDR3.BIT.B1 = 1;
// 左モーター停止
IO.PDR3.BIT.B2 = 1;
IO.PDR3.BIT.B3 = 1;
// タイマZのch0をカウント停止してPWM動作停止
TZ.TSTR.BIT.STR0 = 0;
return 0;
}
- Motor_init関数ではレジスタ一覧で紹介した内容を元に設定する。初期化段階ではSTR0を0にしておくことでPWMを動作させていない。
- Motor_runStart関数では引数に応じてP30~P33のポートを切り替えている。GRAの値を基準にデューティ比に応じてGRBとGRCの値を決定している。定数値を100.0という浮動小数点型にすることで小数点演算を行うようにしている。
- Motor_runStop関数ではポート制御を停止状態に変更し、STR0に0を設定することでモーター駆動を停止している。
課題2
課題内容
MAINモジュールでモーターモジュールを使えるようにせよ。また、main関数を変更し次の処理を繰り返すようにせよ。走行方向とデューティ比を示す。
- 直進(50%)1秒間
- 停止1秒間
- 左回転(30%)1秒間
- 停止1秒間
- 後退(50%)1秒間
- 停止1秒間
- 右回転(30%)1秒間
- 停止1秒間
main.c
//------------------------------------------------
// 概 要:エントリーポイント
//------------------------------------------------
void main(void)
{
// システム初期化処理
main_init();
while(1)
{
}
}
main_init関数ではモーターの回転方向を制御するためP30~P33のI/OポートのPCR設定を出力にする必要があることに注意。
課題が完成したらビルドを行いビュートローバー上で動作させ、期待動作通りに動くことを確認せよ。
期待動作
直進・左回転・後退・右回転の走行を順番に繰り返すこと。
main.c
#include "iodefine.h"
#include "led.h"
#include "sw.h"
#include "timer.h"
#include "lsen.h"
#include "sci.h"
#include "spk.h"
// モーターモジュールを使用するためインクルード
#include "motor.h"
//------------------------------------------------
// 内部プロトタイプ宣言
//------------------------------------------------
void main_init(void);
//------------------------------------------------
// 概 要:エントリーポイント
//------------------------------------------------
void main(void)
{
// システム初期化処理
main_init();
while(1)
{
Motor_runStart(E_MOTOR_FORWARD, 50);
Timer_waitTime(1000);
Motor_runStop();
Timer_waitTime(1000);
Motor_runStart(E_MOTOR_TURN_LEFT, 30);
Timer_waitTime(1000);
Motor_runStop();
Timer_waitTime(1000);
Motor_runStart(E_MOTOR_BACKARD, 50);
Timer_waitTime(1000);
Motor_runStop();
Timer_waitTime(1000);
Motor_runStart(E_MOTOR_TURN_RIGHT, 30);
Timer_waitTime(1000);
Motor_runStop();
Timer_waitTime(1000);
}
}
//------------------------------------------------
// 概 要:システム初期化
//------------------------------------------------
void main_init(void)
{
//---------------------------------------------------
// ウォッチドッグタイマの停止(消さないこと)
//---------------------------------------------------
WDT.TCSRWD.BYTE = 0x92;
WDT.TCSRWD.BYTE = 0x92;
//---------------------------------------------------
// PCRの設定(PCRは書き込み専用レジスタ)
//---------------------------------------------------
IO.PCR3 = 0x0F; // P30,P31,P32,P33を出力(モーター回転方向制御)
IO.PCR6 = 0x11; // P60,P64を出力
IO.PCR7 = 0x00; // P74を入力
//---------------------------------------------------
// モジュールの初期化(各モジュールの初期化を実施)
//---------------------------------------------------
Led_init();
Sw_init();
Timer_init();
Lsen_init();
Sci_init();
Spk_init();
Motor_init();
}
- motor.hをインクルードする
- main関数ではモーター制御用のインターフェースとタイマのインターフェースを使って走行を切り替えている
- main_init関数ではMotor_init関数の呼び出しを追加することと、PCR3を0x0fで設定することでP30~P33を出力ポートに設定している。

マイコンを基礎から体系的に学びたい方はこちらからどうぞ~