サーボの駆動 / ジョイスティックでサーボを制御する

ジョイスティックから出力される電圧値をAD変換し、それをパルス幅に変換する。

f:id:ti-nspire:20190912115401p:plain:h270

#include <avr/io.h>
#include <util/delay.h>
#include "mylib.h"

// パルス幅の範囲を決めておく。
#define PULSE_MIN 1100
#define PULSE_MID 1500
#define PULSE_MAX 1900

// ジョイスティックの実測値
#define X_MIN 254
#define X_MID 523
#define X_MAX 782
#define Y_MIN 244
#define Y_MID 518
#define Y_MAX 791

void initTimer1Servo(){
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);   // 16ビット、Fast PWMモード
    TCCR1B |= (1 << CS11);                   // 8MHzを8分周してカウント
    ICR1 = 20000;                            // TOP値。Timer1の周期を20000usecにする。
    TCCR1A |= (1 << COM1A1) | (1 << COM1B1); // PB1、PB2の両端子からPWM波を出力する。
    OCR1A = PULSE_MID;
    OCR1B = PULSE_MID;
}

void enableServo(){
    while(TCNT1 < 3000){;}           // 制御パルスの途中で何かが始まらないようにする。
    DDRB |= (1 << PB1) | (1 << PB2); // 当該ピンのIOをOUTにしてPWMを出力
}

void initADC(){
    ADMUX  |= (1 << REFS0);                // AVCCを基準電圧として使うことにする。
    ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // ADCへ供給するクロックの分周比を決める。ここでは32分周にしておく。
    ADCSRA |= (1 << ADEN);                 // ADCを有効化する。
}

uint16_t readADC(uint8_t ch){
    ADMUX   = ((0xF0 & ADMUX) | ch);       // チャンネル選択ビットを全部一旦クリアしてから目的のチャンネルをセットして、
    ADCSRA |= (1 << ADSC);                 // AD変換を開始して、
    loop_until_bit_is_clear(ADCSRA, ADSC); // AD変換が終わるのを待って、
    return ADC;                            // 10ビット値を返す。
}

int main(){
    initTimer1Servo();
    initADC();

    while(1){
        enableServo();

        // パルス幅が範囲外の値にならないよう念のためcontain()函数を適用しておく。
        OCR1A = contain(map(readADC(PC4), X_MIN, X_MAX, PULSE_MIN, PULSE_MAX), PULSE_MIN, PULSE_MAX);
        OCR1B = contain(map(readADC(PC5), Y_MIN, Y_MAX, PULSE_MIN, PULSE_MAX), PULSE_MIN, PULSE_MAX);
        _delay_ms(10);
    }

    return 0;
}