モータを回す

今回,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.