モータを回す
今回,2つモータを使ってライントレースロボットを走らせます.
回路
PB-6,7で左のモータ,PB-0,1で右のモータを制御します. 停止,正転,逆転,が可能です.
PWMで速度を変更できるようになっています. PD-5,6がPWM用のピンになってます. これがOFFの時は電流が流れません.
モータドライバ
モータドライバは2本の信号で制御します.
| *IN1 | *IN2 | *動作 |
|---|---|---|
| 0 | 0 | 停止 |
| 1 | 0 | 正転 |
| 0 | 1 | 逆転 |
| 1 | 1 | ブレーキ |
ピン
| *AVR | *モータドライバ |
|---|---|
| PB7 | 左 IN1 |
| PB6 | 左 IN2 |
| PD5 | 左 PWM |
| PB0 | 右 IN1 |
| PB1 | 右 IN2 |
| PD6 | 右 PWM |
マクロ
ビットの操作が面倒くさいのでマクロを定義しておきましょう. ビット演算の意味は,C言語入門のビット演算あたりを参照してください.
#define R_OFF (PORTB &= ~0x03)
#define R_FWD (PORTB &= ~0x02 , PORTB |= 0x01)
#define R_REV (PORTB &= ~0x01 , PORTB |= 0x02)
#define R_STOP (PORTB |= 0x3)
#define L_OFF (PORTB &= ~0xC0)
#define L_FWD (PORTB &= ~0x80 , PORTB |= 0x40)
#define L_REV (PORTB &= ~0x40 , PORTB |= 0x80)
#define L_STOP (PORTB |= 0xC0)
これで,R_FWD;やR_REV;と書くだけで,モータの回転を制御できます. これは一例ですので,配線やロボットの作りにあわせて 回転方向やモータの左右は修正してください.
プログラム例1
まずは速度の調節等は気にせずモータを回してみましょう. 先ほどのマクロを使って書いてみます.
#include <avr/io.h>
#define R_OFF (PORTB &= ~0x03)
#define R_FWD (PORTB &= ~0x02 , PORTB |= 0x01)
#define R_REV (PORTB &= ~0x01 , PORTB |= 0x02)
#define R_STOP (PORTB |= 0x3)
#define L_OFF (PORTB &= ~0xC0)
#define L_FWD (PORTB &= ~0x40 , PORTB |= 0x80)
#define L_REV (PORTB &= ~0x80 , PORTB |= 0x40)
#define L_STOP (PORTB |= 0xC0)
// 指定された時間だけ待つ
void wait(uint16_t w){
while(w--){
volatile uint16_t i=200;
while(i--);
}
}
int main()
{
DDRB = 0xFF;
DDRC = 0x00; // ボタン
DDRD = 0xFF;
PORTC |= 3; // プルアップON
PORTD = 0x60;
wait(100); // 少し待つ
while(PINC&1); // ボタンが押されるまで待つ
// 前進
R_FWD;
L_FWD;
wait(1000);
// 後進
R_REV;
L_REV;
wait(1000);
// 右回転
R_REV;
L_FWD;
wait(500);
// 左回転
R_FWD;
L_REV;
wait(800);
// 停止
R_STOP;
L_STOP;
for(;;);
}
PWM
前の例では,モータをON/OFFできましたが,速度を調節することはできませんでした. そこで,「PWM」の出番です.普通に「ピー・ダブリュー・エム」と読みます.
PWMというのはパルス幅変調(Pulse Width Modulation)と言って,高速に電圧を ON/OFFを繰り返すことによって,速度を調節できます. 簡単に言えば,少し進んで,少し停止を繰り返せば,思った速度で動かせるということです. モータの回転速度は,変化するのに少し時間がかかるので,実際には滑らかに動きます.
ONの時間とOFFの時間の比のことを,デューティ比と呼び,ずっとONの状態がデューティー比が 100%の状態です.
AVRでPWM
今回使う,AVRにはPWMの波形を生成する機能があります. そこで,レジスタを適切に設定すればPWMが簡単に出来ます.
今回使うレジスタは,TCNT0,OCR0A,TCCR0A,TCCR0Bの4つです. 詳しい使い方はAVRのデータシートを参照してください.
PWMというのは一定周期でON/OFFを繰り返すので, 普通,マイコンのPWMの機能はタイマーの機能の一部として実装されています.

カウンタの値がOCR0A(もしくはOCR0B)よりも小さいときは「1」で大きくなると「0」になります. つまり,OCR0xの値に比例して,出力が「1」に成っている時間の割合が増えます. 今回使うカウンタは8ビットなので,2^8-1 = 255が最大となります.

他にも幾つか設定があって,「0」と「1」の関係を逆にしたり,周期を変更したりすることも出来ます.
プログラム例2
#include <avr/io.h>
#define R_OFF (PORTB &= ~0x03)
#define R_FWD (PORTB &= ~0x02 , PORTB |= 0x01)
#define R_REV (PORTB &= ~0x01 , PORTB |= 0x02)
#define R_STOP (PORTB |= 0x3)
#define R_DUTY OCR0A
#define L_OFF (PORTB &= ~0xC0)
#define L_FWD (PORTB &= ~0x40 , PORTB |= 0x80)
#define L_REV (PORTB &= ~0x80 , PORTB |= 0x40)
#define L_STOP (PORTB |= 0xC0)
#define L_DUTY OCR0B
// 指定された時間だけ待つ
void wait(uint16_t w){
while(w--){
volatile uint16_t i=200;
while(i--);
}
}
void pwm_init(void){
//timer start
TCNT0 = 0x00; //カウンタ初期化
TCCR0A = (1<<COM0A1) | (1<<COM0B1);
TCCR0A |= (1<<WGM00); //位相基準PWM
TCCR0B = (1<<CS00); //前置分周無し
}
int main()
{
DDRB = 0xFF;
DDRC = 0x00; // ボタン
DDRD = 0xFF;
PORTC |= 3; // プルアップON
pwm_init();
wait(100); // 少し待つ
while(PINC&1); // ボタンが押されるまで待つ
// 前進
R_DUTY=0x40;
L_DUTY=0x40;
R_FWD;
L_FWD;
wait(1000);
R_DUTY=0x80;
L_DUTY=0x80;
wait(1000);
// 後進
R_DUTY=0xFF;
L_DUTY=0xFF;
R_REV;
L_REV;
wait(1000);
// 停止
R_STOP;
L_STOP;
for(;;);
}
PWMの周波数
PWMをON/OFFする周期は,どれくらいにするべきでしょうか? あまりに遅すぎると,モータが振動して音を出したり,速すぎると回路が追いつかなかったりします.
最適な周波数は,モータや回路の時定数から計算できますが,大抵は数十kHzにしておけば 問題無いでしょう.
今回は,8ビットのカウンタを前置分周なしで使っています. AVRは8MHzで動いているので,8000000/256 = 31250となって,大体30KHzですね.
この文書の履歴
- 2006-07-13 作成
- 2006-07-14 説明を少し追加
- 2006-08-05 PWMについて追記