モータを回す

今回,2つモータを使ってライントレースロボットを走らせます.

回路

PB-6,7で左のモータ,PB-0,1で右のモータを制御します. 停止,正転,逆転,が可能です.

PWMで速度を変更できるようになっています. PD-5,6がPWM用のピンになってます. これがOFFの時は電流が流れません.

モータドライバ

モータドライバは2本の信号で制御します.

IN1IN2動作
00停止
10正転
01逆転
11ブレーキ

ピン

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が簡単に出来ます.

今回使うレジスタは,TCNT0OCR0ATCCR0ATCCR0Bの4つです. 詳しい使い方はAVRのデータシートを参照してください.

PWMというのは一定周期でON/OFFを繰り返すので, 普通,マイコンのPWMの機能はタイマーの機能の一部として実装されています.

images/pwm01.png

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

images/pwm02.png

他にも幾つか設定があって,「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ですね.

この文書の履歴

Copyright © 瓶詰堂 all rights reserved.