このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
DC モータは磁石で覆われたケースの中でコイルが磁界を発生させ、回転力を 生じるものです。 コイルが磁石に接近するとブラシによりコイルの極性が反転し、回転を持続す るようになっています。 これをブラシ付きモータと言うこともあります。 DC モータは安価で高出力ですが、ブラシは摩擦で摩耗するため寿命が比較的短 いです。
DC モータは動力を発生させるため、大きなエネルギーを必要とします。その ため、多くの電流が流れます。 例えばマブチモータの FA130 では電源電圧 1.5V に対して、電流は最大で 1A 流れます。 そのため、マイコンには直接接続できません。 通常はリレーやトランジスタのスイッチング回路やそれをユニット化したモー タドライバーを使用します。
バイポーラトランジスタのスイッチング回路において、出力であるコレクタ電 流はベース電流に比例して流れるため、大電流を制御するにはベース電流 を多く流す必要があります。 しかし、制御側が流せる電流にも限りがあるため、一つのトランジスタでは hFEが足りないと目標のコレクタ電流に達してないことになります。 そのため、トランジスタを二段使い、一段目のエミッタを二段目のベースに接 続するダーリントン接続を用います。 一段目のベースに IB を流すと、一段目のコレクタには hFEIBだけ電流が流れるので、エミッタには (1+hFE)IB の電流が流れます。 これが二段目のベースに流れると、二段目のコレクタにはこの hFE 倍流れます。 したがって、一段目、二段目のコレクタ電流の合計は hFEIB+hFE(1+hFE)IB となり、全体を一つのトランジスタとみなすと hFE'=hFE(2+hFE) と大きな利得が得られます。 これにより小さな電流で大きな電流のスイッチングができるようになります。
モータにはコイルが入っています。 コイルに電流を流すと、コイルは磁界としてエネルギーを蓄えます。 そして、電流を切ると磁界を維持するように電圧が発生します。 これをフライバック電圧と言います。 トランジスタのスイッチング回路を直接接続すると、トランジスタで回路を切 断したとき、トランジスタに大きな逆電圧がかかります。 そのため、トランジスタを保護するため、逆電圧は逃す必要があります。
このフライバック電圧に対して、モータをダイオードで短絡するとダイオード に電流が流れるので、 トランジスタには大きな逆電圧がかからずに済みます。 このモータを短絡するダイオードをフリーホイールダイオードと 呼びます。
また、さらにトランジスタを逆電圧から守るため、コレクタ・エミッタ間やベー ス・エミッタ間にダイオードを入れることもあります。
モータには様々な種類があり、それを駆動するために、また様々なモータド ライバーの種類があります。
この中でコンピュータなどからディジタル入力を受けて、大きな電圧を出力 するものはバッファと呼ばれます。
IN | OUT |
---|---|
L | 0V |
H | 出力電圧 |
トランジスタによるスイッチング回路においては、入力が L の場合、 0V ではなく、電流を流さないくらいの大きな抵抗値 (ハイインピーダンス)になりますが、 バッファは入力が L の場合は0Vになります。 バッファにもう一つ入力(EN)を付加して、ENがLのときにハイインピーダン スになる3状態のバッファをトライステート・バッファと呼びま す。
IN | EN | OUT |
---|---|---|
X | L | ハイインピーダンス |
L | H | 0V |
H | H | 出力電圧 |
フリーホイールダイオードを入れると、モータの電源を切ったとき、ダイ オードに電流が流れます。 ダイオードでは電圧降下と流れる電流により電力を消費します。 つまり、 これはモータが発電機として仕事をしたことになるので、モータの回転エネル ギーが電気エネルギーに変換されたことになります。 つまり、モータは仕事をした分だけ減速します。
但し、モータはフリーホイールダイオードを接続しても瞬時に回転が止まる訳ではあ りません。 つまり、高速でモータの電源を On-Off を繰り返すと、モータは低速で回転す るようになり、与えた電気エネルギーに対応した仕事をすることになります。 モータをマイコンからスイッチング回路で動作させる際、LED の明るさを変え たように duty 比を変えることでモータの速度を変化できます。
図のように 4 つのスイッチを接続するとスイッチの組み合わせにより次のよ うな 4 種類の制御が可能になります。
このモータの制御回路をH ブリッジ回路と言います。
なお、このスイッチの制御において、この図のようにしてしまうと電源をショー トしてしまいます。 この時に流れる電流を貫通電流と呼びます。 制御を行う上で貫通電流が流れないようにすることが重要です。
さて、これをトランジスタでこれを実現するにはどうすればいいでしょうか? 通常のスイッチング回路は電源側に負荷を接続しますが、この回路では電源側 にもスイッチがあるので、原則通りのスイッチング回路では制御できません。
NPN トランジスタのスイッチング回路ではエミッタを直接接地します。 一方、 PNP トランジスタのスイッチング回路を考えると、図のようにエミッ タは電源に接続することになります。 電源側から、 PNP トランジスタのスイッチング回路、負荷、 NPN トランジス タのスイッチング回路、グランドの順に接続することで二つのスイッチで負荷 を挟むことができるようになります。
このとき、 NPN トランジスタと同等の性能の PNP トランジスタを選定する必 要があります。同等の性能である対になるトランジスタのことを コンプリメント と言います。
このコンプリメントを使った H ブリッジ回路は図の通りです。
但し、このままでは PNP トランジスタを On, Off するにはベース側に負荷に 与える電源と同じ電圧が必要になります。 マイコンで制御するにはマイコンの電圧によるスイッチング回路を付加する必要 があります。
なお、大容量の電流をスイッチするための 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 | Vi<0.4V(ベース電流が流れないベース電圧) | Vi> 0.8V, Ii> 20mA (ベース・エミッタが飽和するベース電圧、ベース電流) | 0.2V以上(ベース・エミッタ飽和電圧の最大値) | |
PNP BTr | Vi>Vo-0.4V | Vi<Vo-0.8V, Ii> -20mA | 0.8+0.2V以上(ON 時のベース電圧+ベースエミッタ間飽和電圧) | |
N型 FET | Vi<1.8V(ドレイン電流が流れないゲート・ソース間電圧) | Vi> 4V (ドレイン電流が飽和するゲート・ソース間電圧) | 自由(FET の場合ドレイン・ソース間は抵抗と等価) | |
P型 FET | 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 のスイッチング回路を入れることで解決できます。
このようにモータを動作させるための回路はスイッチング回路の組み合わせで 構成します。 特に、H ブリッジを組むためには、最低でも 4 つのトランジスタが必要です。 モータをコントロールするためのトランジスタを一つの IC にまとめた物 を モータドライバ と呼びます。 モータドライバにはDC モータ用の H ブリッジもありますが、これとは別にス テッピングモータ用の二組二対の回路の入った物もありますので選定する際は 注意する必要があります。
DCモータ用のモータドライバとして代表的なのが東芝の TA7291 シリーズです。 データシートを参照すると下図のような等価回路が書かれています。 全て NPN のトランジスタによりスイッチングが行われます。 従って、グランド側のスイッチング回路は通常のオープンコレクタのスイッチ ング回路です。 一方、電源側のスイッチング回路はエミッタオープンになっています。 エミッタオープンによりベース電圧 Vref を基準電圧としてモータ に与える電圧をコントロールできます。 しかし、Vs と Vrefの差は常に熱エネルギーに変わっ てしまいます。
以上のように、モータの種類やモータを駆動させる回路など、様々な選択、 構成があることがわかりました。 しかし、抽象的に考えれば、モータというのは軸があって、前に回ったり後 ろに回ったりするもので、ソフトウェアとしては、前転、後転、停止を指示 するのが基本となります。
これをオブジェクト指向で抽象化して、forward, backword, stop メソッド を持つMotor という抽象クラスを作ります。 さらに、単純にこれらを交互に行う test メソッドも作成します。
class Motor {
public:
virtual void forward()=0;
virtual void backward()=0;
virtual void stop()=0;
void test(int t);
};
#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 と同じ)
#include "motor.h"
class SimpleMotor : public Motor {
private:
byte port;
public:
void forward();
void backward();
void stop();
SimpleMotor(byte port);
};
#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);
}
#include "simplemotor.h"
void setup() {
}
void loop() {
static SimpleMotor m(A0);
m.test(1000);
}
2ポートで正転と逆転を切り替えるモータドライバーをコントロールする MotorDriver クラスとテストは次のようになります。
#include "motor.h"
class MotorDriver : public Motor {
private:
byte fport;
byte bport;
public:
MotorDriver(byte f, byte b);
void forward();
void backward();
void stop();
};
#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);
}
#include "motordriver.h"
void setup() {
}
void loop() {
static MotorDriver m(A0,A1);
m.test(1000);
}
4つのトランジスタで作ったHブリッジをコントロールする DiscreteDriver クラスとテストは次のようになります。
#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();
};
#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にする。
#include "discretedriver.h"
void setup() {
}
void loop() {
static DiscreteDriver m(A0,A1,A2,A3);
m.test(1000);
}
以下の回路を組み、モータの特性を調べます。 A,B と C,D にオシロスコープをつなぎます。 スイッチを入り切りして、その時の電圧の変化を調べます。
機材 | 数量 |
---|---|
ブレッドボード | 一式 |
マブチモータ FA-130RA | 1個 |
ダイオード 1N4007 | 1本 |
基板用タクトスイッチ | 1個 |
0.1Ω 5W抵抗 | 1本 |
3V(電池ケース+電池) | 1式 |
オシロスコープ | 1式 |
実験 9-1 のスイッチの部分をパワー MOS FET に変更し、マイコンで制御します。 マイコンには演習 7-3 のスイッチにより Duty 比が変わるプログラムを用意し、 AD0 に FET につなぎます。 Duty 比や全体の周期を切替えて、どのようにモータが回り、どのように電圧 が変化するか調べなさい。
機材 | 数量 |
---|---|
ブレッドボード | 一式 |
マブチモータ FA-130RA | 1個 |
パワー MOS FET 2SK4017 | 1石 |
ダイオード 1N4007 | 1本 |
22kΩ | 1本 |
0.1Ω 5W抵抗 | 1本 |
3V電池ボックス | 1式 |
なお、ここで指定している FET の 2SK4017 は廃番予定になっている。 必要に応じて、別のFETを用いる場合、代替品としては以下の仕様のものを選 択すること。
LEDの例6-5または演習6-2 のプログラムをそのまま使います。
パワー MOS FET は静電気に弱いので取扱いに注意すること
実験 9-2 のスイッチング回路を電源側の PNP トランジスタの構成に変更します。 マイコンの制御は実験 9-2 同様に行います。
電源マークのうち、黒には電源装置を繋ぎます。 モータに 1.5V をかけるには何 V の電源が必要か測定しなさい。
機材 | 数量 |
---|---|
ブレッドボード | 一式 |
マブチモータ FA-130RA | 1個 |
トランジスタ 2SB1018A | 1石 |
トランジスタ 2SC1815 | 1石 |
ダイオード 1N4007 | 2本 |
100Ω | 1本 |
1kΩ | 3本 |
0.1Ω 5W抵抗 | 1本 |
3V電池ボックス | 1式 |
ledの例6-5または演習6-2 のプログラムをそのまま使います。
抵抗値 R の算出方法は次の通りです。 モータの適正負荷時の電流は 500mA です。 スイッチング回路なので、2SB1018A を飽和させるには 1A 以上のコレクタ電 流が望めるようにベース電流を考えます。 2SB1018A の hFE はコレクタ電流が 1A の時、 70 から 240 です。 そのため、ベース電流は最大 1A/70=14.3...mA 必要になります。 ここで、余裕を見てベース電流に 20 mA 流すことにします。 2SB1018A の電源電圧を仮に 3.0V と仮定します。 すると、トランジスタが On になるとき、ベースの電圧は 3.0-0.8Vになりま す。 従って、求める抵抗にかかる電圧は 2.2V、流す電流は 20mA なので、 R=2.2/0.02=110 Ω となります。 E24 系列ではそのままこれでも良いですが、大目に電流を流す分には問題ない ので、 E12(E6) 系列で R=100Ω とします。
モータドライバを使ったモータのコントロールを考える。
モータドライバとして、テキサスインスツルメンツのSN754410を選びました。 これは、世界的にポピュラーで
図のようにモータドライバ TA7291P を使ってモータの制御を行いなさい。 二つのスイッチを使ってモータの動作を制御しなさい。 なお、 LED は動作確認用です。
機材 | 数量 |
---|---|
ブレッドボード | 一式 |
マブチモータ FA-130RA | 1個 |
モータドライバ TA7291P | 1個 |
10kΩ | 3本 |
0.1Ω 5W抵抗 | 1本 |
3Ω 5W抵抗 | 1本 |
10μF 電解コンデンサ | 2本 |
3V電池ボックス | 1式 |
モータのコントロールは AD0、AD1 で 行うものとします。 GNDと5VとAD0, AD1をブレッドボードに接続します。
データシートにあるように、入力を切替える際に貫通電流が流れますので、指 示通り切替時に STOP モードに移るようにします。
トランジスタを一定時間 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 onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
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
#include <avr/sleep.h>
#include <MsTimer2.h>
// put your setup code here, to run once:
const byte cport[]={14,15,16,17,0xff};
const byte out7seg[]={6,7,8,9,10,11,12,13,14,15,16,17,0xff};
const byte dutydata[]={1,2,4,8,16,32,64,128,0};
const byte button1=4;
const byte button2=5;
void setup() {
for(byte i=0;out7seg[i]!=0xff;i++){
pinMode(out7seg[i], OUTPUT);
}
pinMode(button1,INPUT_PULLUP);
pinMode(button2,INPUT_PULLUP);
MsTimer2::set(100,vect);
write7seg(0b11011111);
MsTimer2::start();
}
void write7seg(byte x){
for(byte i=0; i<8;i++){
digitalWrite(out7seg[i],x&1<<i);
}
}
void vect(){
hbridge();
}
//#define pullup 0b00110000
//#define cntmask 0b00001111
#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();
}
R の計算。 R はモータ側のトラブルなどで回路が短絡したときに、 IC を守るための電流 制限抵抗です。 そのため、 Vs が GND に短絡したときに、 IC を流れる電流が最 大定格以下になるようにします。 Pタイプの場合、平均出力電流は 1.0A 以下ですので、Vs=3.0V の 時、 3Ω 以上であれば IC を守れます。 この時、抵抗が消費する電力は 3 W になるので、抵抗の電力は 5W 程度のも のを選ぶ必要があります。
図のように FET とトランジスタなどでH ブリッジを作りなさい。 そして、一方のスイッチを押すと正転し、もう一方のスイッチを押すと逆転す るようにしなさい。 但し、切替える時と、両方スイッチを押した時はブレーキがかかるようにしな さい。
機材 | 数量 |
---|---|
ブレッドボード | 一式 |
マブチモータ FA-130RA | 1個 |
パワー MOS FET 2SK4017 | 2石 |
トランジスタ 2SB1018A | 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 onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
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
#include <avr/sleep.h>
#include <MsTimer2.h>
// put your setup code here, to run once:
const byte cport[]={14,15,16,17,0xff};
const byte out7seg[]={6,7,8,9,10,11,12,13,14,15,16,17,0xff};
const byte button1=4;
const byte button2=5;
void setup() {
for(byte i=0;out7seg[i]!=0xff;i++){
pinMode(out7seg[i], OUTPUT);
}
pinMode(button1,INPUT_PULLUP);
pinMode(button2,INPUT_PULLUP);
MsTimer2::set(100,vect);
write7seg(0b11011111);
MsTimer2::start();
}
void write7seg(byte x){
for(byte i=0; i<8;i++){
digitalWrite(out7seg[i],x&1<<i);
}
}
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();
}