1
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

AtTiny13Aでラジコンサーボのコントロール

はじめに

AtTiny13Aを使用してボリュームの角度に応じてラジコンサーボを動作させるだけのテスト回路を作成します。ラジコンサーボのちょっとしたテストに便利に利用可能です。
image.png

用意するもの

AtTiny13a開発環境

以下の4つです。詳しくはAtmelStudioとUSBtinyISPでAVR(AtMega, ATtin)の開発環境を作るを参照ください。
1. Atmel Studio
2. USBtinyISP
3. AVR
4. ArduinoIDE

接続

ボリュームで電源を分圧しADC2に入力、PB0からサーボ出力するだけです。
image.png

プログラム

高速モード(9.6MHz)で動作させ、100kHzで割り込みをかけます。割り込み100回で1msecとなります。ボリュームのADC読取データを100から210にスケーリングしています。

ServoControlByVolume.cpp
/*
 * ServoControl.cpp
 *
 * Created: 22-Aug-18 09:44:06
 * Author : ikeda
 */ 

// For delay.h
#ifndef F_CPU
#define F_CPU   9600000UL       // 9.6MHz
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

volatile unsigned int Tick; // 100KHz pulse
volatile unsigned int sPulse;   // Servo pulse variable
volatile unsigned int Tick_20ms;    // Servo frame variable

unsigned char AnalogRead8bit(unsigned char Ch);
unsigned int AnalogRead(unsigned char Ch);


int main (void)
{
    sei(); //  Enable global interrupts
    DDRB |= (1<<PB0) | (1<<PB1); // PB0 and PB1 as outputs
    TCCR0A |= (1<<WGM01); // Configure timer 1 for CTC mode
    TIMSK0 |= (1<<OCIE0A); // Enable CTC interrupt
    OCR0A = 95; // Set CTC compare value
    TCCR0B |= (1<<CS00); // No prescaler
    Tick = 0;
    sPulse = 100;
    Tick_20ms = 0;

    unsigned long VolumeRead = 0;
    while(1)
    {
        VolumeRead = AnalogRead(2);

        // 1000 - 2100にスケーリング(1ms - 2.1ms)
        int setValue;
        setValue = 10 + (VolumeRead*2/10);
        sPulse = setValue ;
        _delay_ms(10);
    }
}

ISR(TIM0_COMPA_vect)    // 100 KHz interrupt frequency
{
    if(Tick >= 2000)    // One servo frame (20ms) completed
    {
        Tick = 0;
        Tick_20ms = Tick_20ms + 1;
    }

    Tick = Tick + 1;
    if(Tick <= sPulse)  // Generate servo pulse
    {
        PORTB |= (1<<PB0);  // Servo pulse high
    }
    else
    {
        PORTB &= ~(1<<PB0); // Servo pulse low
    }
}

// https://blog.goo.ne.jp/paramako/e/aaea8ec6c7618ca6e083c95776c551d1
unsigned char AnalogRead8bit(unsigned char Ch)
{
    //8bit分解能で十分なのを前提に設定を行っている。

    //デジタル入出力からの切り離し
    DIDR0
    = (0<<ADC0D)    //ADC0ピンの切り離し。しかし、ADC0はリセット端子になってるから設定しちゃダメ
    | (0<<ADC1D)    //同ADC1
    | (1<<ADC2D)    //同・・
    | (0<<ADC3D);   //同・・

    ADMUX
    = (0<<REFS0)    //Reference: 0でVCC参照, 1で1.1V内部電圧源
    | (1<<ADLAR)    //ADC Left Adjust Result: 1にセットすると左詰で結果を出してくる
    | (Ch);         //ADMUX1, ADMUX0で入力チャンネルを選べる 00から11までADC0からADC3まで対応

    ADCSRA
    = (1<<ADEN)     //ADC enable
    | (1<<ADSC)     //ADC Start Conversion 1にすると変換開始
    | (0<<ADATE)    //ADC Auto Trigger Enable (for Continuous Conversion)
    | (0<<ADIF)     //ADC Interrupt Flag    変換完了すると1になるらしい
    | (0<<ADIE)     //ADC Interrupt Enable 変換完了したときに割り込み許可
    | (0b100);      //Clock Division: ADコンバータはクロック50-200kHzで性能がよい
    //分周は0のとき2、それ以上の時2^n分周    1.2MHz(Default)のとき16分周(100)で75kHz。9.6MHzのとき128分周(111)で75kHz
    loop_until_bit_is_set(ADCSRA,ADIF); //ADIFビットが1になるまでムダループ
    return ADCH;
}



unsigned int AnalogRead(unsigned char Ch)
{
    //10bit分解能用ルーチン

    //デジタル入出力からの切り離し
    DIDR0
    = (0<<ADC0D)    //ADC0ピンの切り離し。しかし、ADC0はリセット端子になってるから設定しちゃダメ
    | (0<<ADC1D)    //同ADC1
    | (1<<ADC2D)    //同・・
    | (0<<ADC3D);   //同・・

    ADMUX
    = (0<<REFS0)    //Reference: 0でVCC参照, 1で1.1V内部電圧源
    | (0<<ADLAR)    //ADC Left Adjust Result: 1にセットすると左詰で結果を出してくる
    | (Ch);         //ADMUX1, ADMUX0で入力チャンネルを選べる 00から11までADC0からADC3まで対応

    ADCSRA
    = (1<<ADEN)     //ADC enable
    | (1<<ADSC)     //ADC Start Conversion 1にすると変換開始
    | (0<<ADATE)    //ADC Auto Trigger Enable (for Continuous Conversion)
    | (0<<ADIF)     //ADC Interrupt Flag    変換完了すると1になるらしい
    | (0<<ADIE)     //ADC Interrupt Enable 変換完了したときに割り込み許可
    | (0b100);      //Clock Division: ADコンバータはクロック50-200kHzで性能がよい

    //分周は0のとき2、それ以上の時2^n分周    1.2MHz(Default)のとき16分周(100)で75kHz。9.6MHzのとき128分周(111)で75kHz
    loop_until_bit_is_set(ADCSRA,ADIF); //ADIFビットが1になるまでムダループ

    return ADC;

}

Fuse設定

初期値はクロック設定が低速になっています(1.2MHz)ので、高速(9.6MHz)にする必要があります。

FUSE_INITIAL(LOWSPEED)
CKSEL1 = 1
CKSEL0 = 0 

これを高速に設定する必要があります。

FUSE_HIGHSPEED
CKSEL1 = 0
CKSEL0 = 1

テスト

HDDのブラシレスモータをラジコン用ブラシレスモータコントローラで回してみました。最高速にするとちょっと怖いです。

image.png

基板に

ユニバーサル基板に載せて完成です。
image.png

おわりに

このようなツールを作っておくとデバッグに役に立つかと思います。

2018/08/28 Ikeda

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?