第 10 回 モータの制御

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

10-1. DC モータ

DC モータの特性

DC モータは磁石で覆われたケースの中でコイルが磁界を発生させ、回転力を 生じるものです。 コイルが磁石に接近するとブラシによりコイルの極性が反転し、回転を持続す るようになっています。 これをブラシ付きモータと言うこともあります。 DC モータは安価で高出力ですが、ブラシは摩擦で摩耗するため寿命が比較的短 いです。

DC モータは動力を発生させるため、大きなエネルギーを必要とします。その ため、多くの電流が流れます。 例えばマブチモータの FA130 では電源電圧 1.5V に対して、電流は最大で 1A 流れます。 そのため、マイコンには直接接続できません。 通常はリレーやトランジスタのスイッチング回路やそれをユニット化したモー タドライバーを使用します。

ダーリントン接続

ダーリントン接続

バイポーラトランジスタのスイッチング回路において、出力であるコレクタ電 流はベース電流に比例して流れるため、大電流を制御するにはベース電流 を多く流す必要があります。 しかし、制御側が流せる電流にも限りがあるため、一つのトランジスタでは hFEが足りないと目標のコレクタ電流に達してないことになります。 そのため、トランジスタを二段使い、一段目のエミッタを二段目のベースに接 続するダーリントン接続を用います。 一段目のベースに IB を流すと、一段目のコレクタには hFEIBだけ電流が流れるので、エミッタには (1+hFE)IB の電流が流れます。 これが二段目のベースに流れると、二段目のコレクタにはこの hFE 倍流れます。 したがって、一段目、二段目のコレクタ電流の合計は hFEIB+hFE(1+hFE)IB となり、全体を一つのトランジスタとみなすと hFE'=hFE(2+hFE) と大きな利得が得られます。 これにより小さな電流で大きな電流のスイッチングができるようになります。

フライバック(fly back)電圧とフリーホイール(free wheel)ダイオード

モータにはコイルが入っています。 コイルに電流を流すと、コイルは磁界としてエネルギーを蓄えます。 そして、電流を切ると磁界を維持するように電圧が発生します。 これをフライバック電圧と言います。 トランジスタのスイッチング回路を直接接続すると、トランジスタで回路を切 断したとき、トランジスタに大きな逆電圧がかかります。 そのため、トランジスタを保護するため、逆電圧は逃す必要があります。

通電時 フリーホイールダイオード

このフライバック電圧に対して、モータをダイオードで短絡するとダイオード に電流が流れるので、 トランジスタには大きな逆電圧がかからずに済みます。 このモータを短絡するダイオードをフリーホイールダイオードと 呼びます。

保護ダイオード

また、さらにトランジスタを逆電圧から守るため、コレクタ・エミッタ間やベー ス・エミッタ間にダイオードを入れることもあります。

モータドライバ

モータには様々な種類があり、それを駆動するために、また様々なモータド ライバーの種類があります。

この中でコンピュータなどからディジタル入力を受けて、大きな電圧を出力 するものはバッファと呼ばれます。

INOUT
L0V
H出力電圧

トランジスタによるスイッチング回路においては、入力が L の場合、 0V ではなく、電流を流さないくらいの大きな抵抗値 (ハイインピーダンス)になりますが、 バッファは入力が L の場合は0Vになります。 バッファにもう一つ入力(EN)を付加して、ENがLのときにハイインピーダン スになる3状態のバッファをトライステート・バッファと呼びま す。

INENOUT
XLハイインピーダンス
LH0V
HH出力電圧

PWM 駆動

フリーホイールダイオードを入れると、モータの電源を切ったとき、ダイ オードに電流が流れます。 ダイオードでは電圧降下と流れる電流により電力を消費します。 つまり、 これはモータが発電機として仕事をしたことになるので、モータの回転エネル ギーが電気エネルギーに変換されたことになります。 つまり、モータは仕事をした分だけ減速します。

但し、モータはフリーホイールダイオードを接続しても瞬時に回転が止まる訳ではあ りません。 つまり、高速でモータの電源を On-Off を繰り返すと、モータは低速で回転す るようになり、与えた電気エネルギーに対応した仕事をすることになります。 モータをマイコンからスイッチング回路で動作させる際、LED の明るさを変え たように duty 比を変えることでモータの速度を変化できます。

正逆転制御

Hブリッジ

図のように 4 つのスイッチを接続するとスイッチの組み合わせにより次のよ うな 4 種類の制御が可能になります。

正転
正転
逆転
逆転
停止
停止
ブレーキ
ブレーキ
モータをショートすると回転エネルギーが電気エネルギーに変換されるた め、ブレーキがかかります

このモータの制御回路をH ブリッジ回路と言います。

貫通電流

なお、このスイッチの制御において、この図のようにしてしまうと電源をショー トしてしまいます。 この時に流れる電流を貫通電流と呼びます。 制御を行う上で貫通電流が流れないようにすることが重要です。

トランジスタのスイッチング回路(発展)

さて、これをトランジスタでこれを実現するにはどうすればいいでしょうか? 通常のスイッチング回路は電源側に負荷を接続しますが、この回路では電源側 にもスイッチがあるので、原則通りのスイッチング回路では制御できません。

コンプリメント

npnスイッチング回路 pnpスイッチング回路 pnpnpnスイッチング回路

NPN トランジスタのスイッチング回路ではエミッタを直接接地します。 一方、 PNP トランジスタのスイッチング回路を考えると、図のようにエミッ タは電源に接続することになります。 電源側から、 PNP トランジスタのスイッチング回路、負荷、 NPN トランジス タのスイッチング回路、グランドの順に接続することで二つのスイッチで負荷 を挟むことができるようになります。

このとき、 NPN トランジスタと同等の性能の PNP トランジスタを選定する必 要があります。同等の性能である対になるトランジスタのことを コンプリメント と言います。

Hブリッジ

このコンプリメントを使った H ブリッジ回路は図の通りです。

但し、このままでは PNP トランジスタを On, Off するにはベース側に負荷に 与える電源と同じ電圧が必要になります。 マイコンで制御するにはマイコンの電圧によるスイッチング回路を付加する必要 があります。

FETによるHブリッジ

なお、大容量の電流をスイッチするための FET は パワーMOS FET と言います。 通常は N 型のパワー MOS FET を使いますが、このようにコンプリメントが必 要なときは P 型を使います。 なお、MOS FET は高性能ですが、静電気に弱いので取扱いに注意する必要があ ります。

参考: エミッタオープン回路

トランジスタによる定電流回路はベースの電圧に応じて、コレクタに(つまりエ ミッタにも)一定の電流を流す物でした。 ここで、コレクタを直接電源につなぎ、エミッタ側に負荷を接続すると、ベー スの電圧が高いときはベース・エミッタ間電圧が VBE(≒0.6V)に なるように電流が流れ、ベースの電圧が低いときは電流が流れないようになり ます。 この回路は通常 エミッタフォロワ と呼ばれます。 ベースの電圧とエミッタの電圧の変動幅は同じになるので増幅率は 1 ですが、 高周波特性が良く、出力インピーダンスが低いので、オーディオの回路の出力 段等で良く用いられます。 但し、トランジスタは完全に On にならずにベースの電流に比例してコレクタ の電流は流れる状態になります。 そのため、コレクタ・エミッタ間に発生する電圧降下の分、トランジスタに熱 が発生します。 従って、モータの制御等でこの回路を使う際には特にトランジスタの熱対策が 必要になります。

低電圧電源の電源側スイッチング

スイッチング回路では、回路の構成により、信号側の電圧(Vi)と スイッチされる電源側の電圧(Vo)を異なるように設定できます。 バイポーラトランジスタの NPN, PNP と FET の N型, P型の各回路での 制約をまとめると以下のようになります(但し Vi, Vo≥ 0 と仮定します)。

トランジスタ型回路 Vi(OFF) Vi(ON) Vo の制約
NPN BTr npnスイッチング回路 Vi<0.4V(ベース電流が流れないベース電圧) Vi> 0.8V, Ii> 20mA (ベース・エミッタが飽和するベース電圧、ベース電流) 0.2V以上(ベース・エミッタ飽和電圧の最大値)
PNP BTr pnpスイッチング回路 Vi>Vo-0.4V Vi<Vo-0.8V, Ii> -20mA 0.8+0.2V以上(ON 時のベース電圧+ベースエミッタ間飽和電圧)
N型 FET k型スイッチング回路 Vi<1.8V(ドレイン電流が流れないゲート・ソース間電圧) Vi> 4V (ドレイン電流が飽和するゲート・ソース間電圧) 自由(FET の場合ドレイン・ソース間は抵抗と等価)
P型 FET j型スイッチング回路 Vi>Vo-1.8V(ドレイン電流が流れないゲート・ ソース間電圧) Vi< Vo-4V (ドレイン電流が飽和するゲート・ソース間電圧) 4V以上(ON 時のゲート・ソース間電圧)
各スイッチング回路の電気的制限

マイコンで駆動する限り NPN, N型に関しては大した差はありません。 特に大電流が必要な場合、 NPN トランジスタだとコレクタ電流に応じてベー ス電流を多く流す必要が出てきますが、 N型 FET では定められた電圧をかけ ることが全てで、電流はほとんど流れません。 したがって、 N 型 FET の方が望ましいと言えます。 一方、電源側でスイッチングする PNP, P 型に関しては、 Vo の 制約が出てきます。 特に、 P 型 FET では 4V 以上無いと動作できません。 従って、模型のような 1.5V から 3V の電源は P 型 FET で組んだブリッジではコントロールできませ ん。 つまり、模型用のモータなどを駆動する時に、電源側のスイッチングを行うには PNP 型のトランジスタを用いる必要があります。 なお、 PNP 型の入力には Vo の制限がありますが、以下のように、 入力側に NPN のスイッチング回路を入れることで解決できます。

k型スイッチング回路

モータドライバ

このようにモータを動作させるための回路はスイッチング回路の組み合わせで 構成します。 特に、H ブリッジを組むためには、最低でも 4 つのトランジスタが必要です。 モータをコントロールするためのトランジスタを一つの IC にまとめた物 を モータドライバ と呼びます。 モータドライバにはDC モータ用の H ブリッジもありますが、これとは別にス テッピングモータ用の二組二対の回路の入った物もありますので選定する際は 注意する必要があります。

DCモータ用のモータドライバとして代表的なのが東芝の TA7291 シリーズです。 データシートを参照すると下図のような等価回路が書かれています。 全て NPN のトランジスタによりスイッチングが行われます。 従って、グランド側のスイッチング回路は通常のオープンコレクタのスイッチ ング回路です。 一方、電源側のスイッチング回路はエミッタオープンになっています。 エミッタオープンによりベース電圧 Vref を基準電圧としてモータ に与える電圧をコントロールできます。 しかし、Vs と Vrefの差は常に熱エネルギーに変わっ てしまいます。

TA7291

モータの制御

以上のように、モータの種類やモータを駆動させる回路など、様々な選択、 構成があることがわかりました。 しかし、抽象的に考えれば、モータというのは軸があって、前に回ったり後 ろに回ったりするもので、ソフトウェアとしては、前転、後転、停止を指示 するのが基本となります。

これをオブジェクト指向で抽象化して、forward, backword, stop メソッド を持つMotor という抽象クラスを作ります。 さらに、単純にこれらを交互に行う test メソッドも作成します。

motor.h


class Motor {
public:  
  virtual void forward()=0;
  virtual void backward()=0;
  virtual void stop()=0;
  void test(int t);
};

motor.cpp


#include "Arduino.h"
#include "motor.h"
void Motor::test(int t){
  forward();
  delay(t);
  stop();
  delay(t);
  backward();
  delay(t);
  stop();
  delay(t);
}

例えば、A0 ポートに単純に接続したMOS FETを On-Offするだけの SimpleMotor クラスは次のようになる(backward は stop と同じ)

simplemotor.h


#include "motor.h"
class SimpleMotor : public Motor {
private:
  byte port;
public:
  void forward();
  void backward();
  void stop();
  SimpleMotor(byte port);
};

simplemotor.cpp


#include "Arduino.h"
#include "simplemotor.h"
SimpleMotor::SimpleMotor(byte p):port(p){
  pinMode(port,OUTPUT);
}
void SimpleMotor::forward(){
  digitalWrite(port,HIGH);
}
void SimpleMotor::backward(){
  //not available
  digitalWrite(port,LOW);
}
void SimpleMotor::stop(){
  digitalWrite(port,LOW);
}

inoファイル


#include "simplemotor.h"
void setup() {
}
void loop() {
 static SimpleMotor m(A0);
 m.test(1000);
}

2ポートで正転と逆転を切り替えるモータドライバーをコントロールする MotorDriver クラスとテストは次のようになります。

motordriver.h


#include "motor.h"
class MotorDriver : public Motor {
  private:
  byte fport;
  byte bport;
  public:
  MotorDriver(byte f, byte b);
  void forward();
  void backward();
  void stop();
};

motordriver.cpp


#include "Arduino.h"
#include "motordriver.h"
MotorDriver::MotorDriver(byte f, byte b):fport(f),bport(b){
  pinMode(fport,OUTPUT);
  pinMode(bport,OUTPUT);
}
void MotorDriver::forward(){
  digitalWrite(bport,LOW);
  digitalWrite(fport,HIGH);
}
void MotorDriver::backward(){
  digitalWrite(fport,LOW);
  digitalWrite(bport,HIGH);
}
void MotorDriver::stop(){
  digitalWrite(fport,LOW);
  digitalWrite(bport,LOW);
}

inoファイル


#include "motordriver.h"
void setup() {
}
void loop() {
 static MotorDriver m(A0,A1);
 m.test(1000);
}

4つのトランジスタで作ったHブリッジをコントロールする DiscreteDriver クラスとテストは次のようになります。

discretedriver.h


#include "motor.h"
class DiscreteDriver : public Motor {
  private:
  byte port0;
  byte port1;
  byte port2;
  byte port3;
  public:
  DiscreteDriver(byte p0, byte p1, byte p2, byte p3);
  void forward();
  void backward();
  void stop();
};

discretedriver.cpp


#include "Arduino.h"
#include "discretedriver.h"
DiscreteDriver::DiscreteDriver(byte p0, byte p1, byte p2, byte p3)
:port0(p0),port1(p1),port2(p2),port3(p3){
  pinMode(port0,OUTPUT);
  pinMode(port1,OUTPUT);
  pinMode(port2,OUTPUT);
  pinMode(port3,OUTPUT);
}
void DiscreteDriver::forward(){
  digitalWrite(port1,LOW);
  digitalWrite(port3,LOW);
  digitalWrite(port0,HIGH);
  digitalWrite(port2,HIGH);
}
void DiscreteDriver::backward(){
  digitalWrite(port0,LOW);
  digitalWrite(port2,LOW);
  digitalWrite(port1,HIGH);
  digitalWrite(port3,HIGH);
}
void DiscreteDriver::stop(){
  digitalWrite(port0,LOW);
  digitalWrite(port1,LOW);
  digitalWrite(port2,LOW);
  digitalWrite(port3,LOW);
}

貫通電流が流れないように、OffにしてからOnにする。

inoファイル


#include "discretedriver.h"
void setup() {
}
void loop() {
  static DiscreteDriver m(A0,A1,A2,A3);
  m.test(1000);
}

10-2. 実験

実験10-1

以下の回路を組み、モータの特性を調べます。 A,B と C,D にオシロスコープをつなぎます。 スイッチを入り切りして、その時の電圧の変化を調べます。

回路図

実験1

回路

実験1回路

測定例

実験1測定例

部品表

機材 数量
ブレッドボード 一式
マブチモータ FA-130RA 1個
ダイオード 1N4007 1本
基板用タクトスイッチ 1個
0.1Ω 5W抵抗 1本
3V(電池ケース+電池) 1式
オシロスコープ 1式

実験10-2

実験 10-1 のスイッチの部分をパワー MOS FET に変更し、マイコンで制御します。 マイコンには演習 9-5 のスイッチにより Duty 比が変わるプログラムを用意し、 AD0 に FET につなぎます。 Duty 比や全体の周期を切替えて、どのようにモータが回り、どのように電圧 が変化するか調べなさい。

回路図

実験2

回路

実験2回路

部品表

機材 数量
ブレッドボード 一式
マブチモータ FA-130RA 1個
パワー MOS FET 2SK4017 1石
ダイオード 1N4007 2本
22kΩ 1本
0.1Ω 5W抵抗 1本
3V電池ボックス 1式

なお、ここで指定している FET の 2SK4017 は廃番予定になっている。 必要に応じて、別のFETを用いる場合、代替品としては以下の仕様のものを選 択すること。

  1. Nチャネル
  2. 4V以下の駆動
  3. ドレイン電流 3A 以上
  4. オン抵抗が低い(0.2Ω以下など)
  5. オン状態で1Aの電流を流しつづけても安全なこと

プログラム

LEDの例6-5または演習6-2 のプログラムをそのまま使います。

単純にON, OFFをするプログラム

const byte out[] = {A0, A1, A2, 0xff};    
void setup(){
  for(byte i=0; out[i] != 0xff; i++){
    pinMode(out[i], OUTPUT);
  }
}
void writePattern(byte p){
  byte b=1;
  for(byte i = 0; out[i] != 0xff; i++){
    digitalWrite(out[i], (p&b) != 0);
    b<<=1;
  }
}
void loop(){
  for(byte j=0; j<=7; j++){
    writePattern(j);
    delay(1000);
  }
}		     
Duty 比を変えるプログラム

#include <avr/sleep.h>
#include <MsTimer2.h>
  // put your setup code here, to run once:
const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
const byte dutydata[]={1,2,4,8,16,32,64,128,0xff};
const byte pattern[]={
      0b11011110, //0
      0b10010000, //1
      0b11001101, //2
      0b11011001, //3
      0b10010011, //4
      0b01011011, //5
      0b01011111, //6
      0b11010000, //7
      0b11011111, //8
      0b11011011, //9
      0b11010111, //A
      0b00011111, //b
      0b01001110, //C
      0b10011101, //d
      0b01001111, //E
      0b01000111 //F
};
const byte button1=4;
const byte button2=5;
byte counter;
byte dcycle;
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  pinMode(A0,OUTPUT);
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  counter=0;
  MsTimer2::set(1,vect);
  write7seg(0b11011111);
  MsTimer2::start();
}
void write7seg(byte x){
  for(byte i=0; i<8;i++){
    digitalWrite(out7seg[i],x&1<<i);
  }
}


#define drate 1
byte dutycounter;
void duty(){
  dutycounter++;
  if(dutycounter==dcycle){
    dutycounter=0;
  }
  if(dutycounter<drate){
    digitalWrite(A0,HIGH);
  }else{
    digitalWrite(A0,LOW);
  }
}

void vect(){
  duty();
}

void increase(){
  if(dutydata[counter]==0xff){
    counter=0;
  }else{
    counter++;
  }
}
void decrease(){
  if(counter==0){
    counter=sizeof(dutydata)/sizeof(dutydata[0])-1;
  }else{
    counter--;
  }
}

void setduty(){
  dcycle = dutydata[counter];
}
void loop() {
  static bool prev1=HIGH;
  static bool prev2=HIGH;
  static bool now1=HIGH;
  static bool now2=HIGH;
  setduty();
  now1=digitalRead(button1);
  if(now1==HIGH && prev1==LOW){
    decrease();
  }
  prev1=now1;
  now2=digitalRead(button2);
  if(now2==HIGH && prev2==LOW){
    increase();
  }
  write7seg(pattern[counter]);
  prev2=now2;
}
注意点

パワー MOS FET は静電気に弱いので取扱いに注意すること

実験10-3

実験 10-2 のスイッチング回路を電源側の PNP トランジスタの構成に変更します。 マイコンの制御は実験 10-2 同様に行います。

電源マークのうち、黒には電源装置を繋ぎます。 モータに 1.5V をかけるには何 V の電源が必要か測定しなさい。

回路図

実験3

回路

実験3回路

部品表

機材 数量
ブレッドボード 一式
マブチモータ FA-130RA 1個
トランジスタ 2SA1441 1石
トランジスタ 2SC1815 1石
ダイオード 1N4007 2本
100Ω 1本
1kΩ 3本
0.1Ω 5W抵抗 1本
3V電池ボックス 1式

プログラム

ledの例6-5または演習6-2 のプログラムをそのまま使います。

補足

抵抗値 R の算出方法は次の通りです。 モータの適正負荷時の電流は 500mA です。 スイッチング回路なので、2SA1441 を飽和させるには 1A 以上のコレクタ電 流が望めるようにベース電流を考えます。 2SA1441 の hFE はコレクタ電流が 1A の時、 70 から 240 です。 そのため、ベース電流は最大 1A/70=14.3...mA 必要になります。 ここで、余裕を見てベース電流に 20 mA 流すことにします。 2SA1441 の電源電圧を仮に 3.0V と仮定します。 すると、トランジスタが On になるとき、ベースの電圧は 3.0-0.8Vになりま す。 従って、求める抵抗にかかる電圧は 2.2V、流す電流は 20mA なので、 R=2.2/0.02=110 Ω となります。 E24 系列ではそのままこれでも良いですが、大目に電流を流す分には問題ない ので、 E12(E6) 系列で R=100Ω とします。

実験 9-4

モータドライバを使ってモータのコントロールをします。

モータドライバとして、テキサスインスツルメンツのSN754410を選びました。 これは、世界的にポピュラーなモータドライバーで、Arudino を使った製作例 も公開されています。 なお、このICの内部は電圧の低い側はスイッチング回路ですが、高い側がエ ミッタフォロワーになっているため、電圧降下が大きいです。 そのため、モータの電源として、4.5Vの電池を使用すると、3V程度が出力され、ちょうどモータの定格と合致します。

回路図

実験4

回路

実験4回路

部品表

機材 数量
ブレッドボード 一式
マブチモータ FA-130RA 1個
モータドライバ SN754410 1個
0.1μFコンデンサ 1本
4.5V電池ボックス 1式
タクトスイッチ 2個
arduino 1式

プログラム

モータのコントロールは AD0、AD1 で 行うものとします。 GNDと5VとAD0, AD1をブレッドボードに接続します。

データシートにあるように、入力を切替える際に貫通電流が流れますので、指 示通り切替時に STOP モードに移るようにします。

トランジスタを一定時間 Off にする必要があるので、ここでは割り込みのタ イミングで次のような動作をするとします。

  1. スイッチの位置が保存した物と変わってなければ処理終了
  2. Off になってなければ Off にして処理終了
  3. スイッチの位置を保存する
  4. PortD4 が Onの時
    1. PortD5 が On ならブレーキ
    2. PortD5 が Off なら正転
  5. PortD4 が Offの時
    1. PortD5 が On なら逆転
    2. PortD5 が Off なら Off

なお、データシートで指定されている一定時間は 100μs です。 ATtiny2313 は DIV8ヒューズビットが on の時(デフォルト値)ではプ リスケーラ無しでも 256μs 間隔で割り込みがかかります。 この場合は、割り込み一 回分だけOff にすれば良いです。


;**********
;*  実験9-4 *
;**********
.cseg
.org 0x0000
	rjmp reset
.org	OVF2addr
	rjmp	timer2vec
.org INT_VECTORS_SIZE
reset:
	ldi	r16,high(RAMEND)
	out	SPH,r16
	ldi	r16,low(RAMEND)
	out	SPL,r16

.equ	pullup	= 0b00110000
.equ	onc	= 0b00001111

	ldi		r16,pullup
	out		portd, r16
	ldi		r16,onc
	out		ddrc, r16

initsleep:
	in	r16,smcr
	cbr	r16,1<<sm0
	cbr	r16,1<<sm1
	cbr	r16,1<<sm2
	sbr	r16,1<<se
	out	smcr,r16

inittimer2:
	ldi	r16,0b00000000	;normal mode
	sts	tccr2a,r16
	ldi	r16,0b00000111	; normal mode, prescaler=1
	sts	tccr2b, r16
	ldi	r16, 0b00000001 ; enable interruption for overflow
	sts	timsk2, r16


.macro	outport
	ldi	r20,@0
	and	r20,r16	
	in	r21,@1
	andi	r21,~@0
	or	r21,r20
	out	@1,r21
.endmacro

	ldi	r16,0b11110111
	outport	onb,portb
	outport	ond,portd
	sei
main:
	sleep	
	rjmp	main

timer2vec:
	rcall	hbridgecnt
	reti
 
.def	work = r16
.def	now = r17
.def	prev = r18

.equ	cntmask		=	0b00000011
.equ	offpattern	=	0b00000000
.equ	breakon		=	0b00000011
.equ	seiten		=	0b00000001
.equ	gyakuten	=	0b00000010

hbridgecnt:
	in		now,PIND	
;	mov		now,work
	andi	now,pullup
	cp		now,prev
	brne	h1
	ret
h1:
	in		work,PORTC	
	andi	work,cntmask
	brne	setoff
	mov		prev,now
readkey:
	sbrs	now,4
	rjmp	rd4off
rd4on:
	sbrs	now,5
	rjmp	rd4on5off
rd4on5on:	
setoff:
	ldi		r16, offpattern
	outport onc,portc
	ret
rd4on5off:	
	ldi		r16, seiten
	outport onc,portc
	ret
rd4off:
	sbrs	now,5
	rjmp	rd4off5off
rd4off5on:	
	ldi		r16, gyakuten
	outport onc,portc
	ret
rd4off5off:	
	ldi		r16, breakon
	outport onc,portc
	ret
.exit

C言語版


#include <avr/sleep.h>
#include <MsTimer2.h>
const byte cport[]={A0,A1,A2,A3,0xff};
const byte button1=4;
const byte button2=5;
void setup() {
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  for(byte i=0;cport[i]!=0xff;i++){
    pinMode(cport[i], OUTPUT);
  }
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
  MsTimer2::set(100,vect);
  MsTimer2::start();
}
void vect(){
  hbridge();
}
#define offpattern 0b00000000
#define breakpattern 0b00000011
#define seitenpattern 0b00000001
#define gyakutenpattern 0b00000010

void setcport(byte pattern){
  for(byte i=0; cport[i]!=0xff; i++){
    digitalWrite(cport[i],pattern&1<<i);
  }
}
byte portc(){
  byte result=0;
  for(byte i=0; cport[i]!=0xff; i++){
    result+=digitalRead(cport[i]); 
  }
  return result;
}
void hbridge(){
  static byte now;
  static byte prev;

  now = digitalRead(button1)<<button1
        |digitalRead(button2)<<button2;
  if(now==prev) return;
  if(portc()){
    setcport(offpattern);
    return;
  }
  prev=now;
  switch(now){
  case 0b00000000:
      setcport(breakpattern);
      return;
  case 0b00010000:
      setcport(seitenpattern);
      return;
  case 0b00100000:
      setcport(gyakutenpattern);
      return;
  case 0b00110000:
      setcport(offpattern);
      return;
  }
}

void loop(){
  sleep_cpu();
}

実験 9-5

図のように FET とトランジスタなどでH ブリッジを作りなさい。 そして、一方のスイッチを押すと正転し、もう一方のスイッチを押すと逆転す るようにしなさい。 但し、切替える時と、両方スイッチを押した時はブレーキがかかるようにしな さい。

回路図

実験5

回路

実験5回路 実験5回路全体

部品表

機材 数量
ブレッドボード 一式
マブチモータ FA-130RA 1個
パワー MOS FET 2SK4017 2石
トランジスタ 2SA1441 2石
トランジスタ 2SC1815 2石
ダイオード 1N4007 4本
100Ω 2本
1kΩ 6本
22kΩ 2本
0.1Ω 5W抵抗 1本
3V電池ボックス 1式

プログラム

マイコンでFET などのトランジスタを制御して、正転、逆転の操作を可能にします。 ここで注意しなければならないのは、絶対に貫通電流を流さないようにするこ とです。 トランジスタの性能は同じ品番でも微妙に異なっているのが普通なので、同時 に On や Off の信号を送っても同時に On や Off になる保証はありません。 そのため、切替時は一定時間すべて Off にする必要があります。

入力スイッチを PortD4、 PortD5 とし、モータのコントロールは AD0 から AD3 で 行うものとします。 接続は回路図の通りとします。

プログラムは実験 9-4 と同様で、出力パターンのみを変えたものになります。


;**********
;*  実験9-5 *
;**********

.cseg
.org 0x0000
	rjmp reset
.org	OVF2addr
	rjmp	timer2vec
.org INT_VECTORS_SIZE
reset:
	ldi	r16,high(RAMEND)
	out	SPH,r16
	ldi	r16,low(RAMEND)
	out	SPL,r16

.equ	pullup	= 0b00110000
.equ	onc	= 0b00001111

	ldi	r16,pullup
	out	portd, r16
	ldi	r16,onc
	out	ddrc, r16

initsleep:
	in	r16,smcr
	cbr	r16,1<<sm0
	cbr	r16,1<<sm1
	cbr	r16,1<<sm2
	sbr	r16,1<<se
	out	smcr,r16

inittimer2:
	ldi	r16,0b00000000	;normal mode
	sts	tccr2a,r16
	ldi	r16,0b00000111	; normal mode, prescaler=1
	sts	tccr2b, r16
	ldi	r16, 0b00000001 ; enable interruption for overflow
	sts	timsk2, r16


.macro	outport
	ldi	r20,@0
	and	r20,r16	
	in	r21,@1
	andi	r21,~@0
	or	r21,r20
	out	@1,r21
.endmacro

	ldi	r16,0b11110111
	outport	onb,portb
	outport	ond,portd
	sei
main:
	sleep	
	rjmp	main

timer2vec:
	rcall	hbridgecnt
	reti

 
 
.def	work = r16
.def	now = r17
.def	prev = r18


.equ	cntmask		=	0b00000011
.equ	offpattern	=	0b00000000
.equ	breakon		=	0b00000101
.equ	seiten		=	0b00000110
.equ	gyakuten	=	0b00001001

hbridgecnt:
	in		now,PIND	
;	mov		now,work
	andi	now,pullup
	cp		now,prev
	brne	h1
	ret
h1:
	in		work,PORTC	
	andi	work,cntmask
	brne	setoff
	mov		prev,now
readkey:
	sbrs	now,4
	rjmp	rd4off
rd4on:
	sbrs	now,5
	rjmp	rd4on5off
rd4on5on:	
setoff:
	ldi		r16, offpattern
	outport onc,portc
	ret
rd4on5off:	
	ldi		r16, seiten
	outport onc,portc
	ret
rd4off:
	sbrs	now,5
	rjmp	rd4off5off
rd4off5on:	
	ldi		r16, gyakuten
	outport onc,portc
	ret
rd4off5off:	
	ldi		r16, breakon
	outport onc,portc
	ret
.exit

C言語版


#include <avr/sleep.h>
#include <MsTimer2.h>
const byte cport[]={A0,A1,A2,A3,0xff};
const byte button1=4;
const byte button2=5;
void setup() {
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  for(byte i=0;cport[i]!=0xff;i++){
    pinMode(cport[i], OUTPUT);
  }
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
  MsTimer2::set(100,vect);
  MsTimer2::start();
}
void vect(){
  hbridge();
}
#define offpattern 0b00000000
#define breakpattern 0b00000101
#define seitenpattern 0b00000110
#define gyakutenpattern 0b00001001
void setcport(byte pattern){
  for(byte i=0; cport[i]!=0xff; i++){
    digitalWrite(cport[i],pattern&1<<i);
  }
}
byte portc(){
  byte result=0;
  for(byte i=0; cport[i]!=0xff; i++){
    result+=digitalRead(cport[i]); 
  }
  return result;
}
void hbridge(){
  static byte now;
  static byte prev;

  now = digitalRead(button1)<<button1
        |digitalRead(button2)<<button2;
  if(now==prev) return;
  if(portc()){
    setcport(offpattern);
    return;
  }
  prev=now;
  switch(now){
  case 0b00000000:
      setcport(breakpattern);
      return;
  case 0b00010000:
      setcport(seitenpattern);
      return;
  case 0b00100000:
      setcport(gyakutenpattern);
      return;
  case 0b00110000:
      setcport(offpattern);
      return;
  }
}

void loop(){
  sleep_cpu();
}

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科