#はじめに
AtTiny13Aを使用してボリュームの角度に応じてラジコンサーボを動作させるだけのテスト回路を作成します。ラジコンサーボのちょっとしたテストに便利に利用可能です。
#用意するもの
##AtTiny13a開発環境
以下の4つです。詳しくはAtmelStudioとUSBtinyISPでAVR(AtMega, ATtin)の開発環境を作るを参照ください。
- Atmel Studio
- USBtinyISP
- AVR
- ArduinoIDE
#接続
ボリュームで電源を分圧しADC2に入力、PB0からサーボ出力するだけです。
#プログラム
高速モード(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のブラシレスモータをラジコン用ブラシレスモータコントローラで回してみました。最高速にするとちょっと怖いです。
#おわりに
このようなツールを作っておくとデバッグに役に立つかと思います。
2018/08/28 Ikeda