今回,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;と書くだけで,モータの回転を制御できます. これは一例ですので,配線やロボットの作りにあわせて 回転方向やモータの左右は修正してください.
まずは速度の調節等は気にせずモータを回してみましょう. 先ほどのマクロを使って書いてみます.
#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(;;); }
前の例では,モータをON/OFFできましたが,速度を調節することはできませんでした. そこで,「PWM」の出番です.普通に「ピー・ダブリュー・エム」と読みます.
PWMというのはパルス幅変調(Pulse Width Modulation)と言って,高速に電圧を ON/OFFを繰り返すことによって,速度を調節できます. 簡単に言えば,少し進んで,少し停止を繰り返せば,思った速度で動かせるということです. モータの回転速度は,変化するのに少し時間がかかるので,実際には滑らかに動きます.
ONの時間とOFFの時間の比のことを,デューティ比と呼び,ずっとONの状態がデューティー比が 100%の状態です.
今回使う,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」の関係を逆にしたり,周期を変更したりすることも出来ます.
#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をON/OFFする周期は,どれくらいにするべきでしょうか? あまりに遅すぎると,モータが振動して音を出したり,速すぎると回路が追いつかなかったりします.
最適な周波数は,モータや回路の時定数から計算できますが,大抵は数十kHzにしておけば 問題無いでしょう.
今回は,8ビットのカウンタを前置分周なしで使っています. AVRは8MHzで動いているので,8000000/256 = 31250となって,大体30KHzですね.