モータを回す
今回,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について追記