2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Attiny13AとArduinoを使って任意の周波数を作ってみる~キャリブレーションに関する覚書~

Last updated at Posted at 2025-01-20

そもそも任意の周波数を作るならpwmでいいんじゃない?

Arduinoで任意の周波数を作るならpwmを使うのが主流のようですし、生成AIに聞いてもたいていpwmとレジストリで実現する方法を教えてくれます。
例えばこんな記事が私にはわかりやすかったです。
しかし、Attiny13Aのようなマイコンだと

'TCCR1B' was not declared in this scope

のようなエラーでコンパイルができません。

私にはPWMは難しすぎる:ATTinyではpwd+レジストリが使えなかった

そこで、仕方なく単純にForループで時間調整をすることにしたのですが、最初は周波数に合わせてForで回す回数を調整するのに勘を頼っていたのでなかなか正確な周波数にならないし、複数の周波数のためのパラメーターを見つけるためには種類分試行錯誤を繰り返す必要がありました。

Forで周波数を調整:勘頼りの調整はやめよう!

試行錯誤だと精度が出なかったことと、作業ががあまりに大変だったので、演繹的にパラメーターを決める方法を考えました。

1. 最初に下記ソースのwavelengthに適当に散らばらせた数字を代入して出力される周波数を測ります。

#define SP 0
const unsigned int calibration[]={
  5, 10, 20, 40, 80, 200, 1000
};

unsigned int wavelength, j;
int timespan;
unsigned char i;


void setup() {
}

void loop() {
  timespan=10; playList(calibration, ARRAY_LENGTH(calibration));
}

void playList(const unsigned int *list, int arrayLength){
  for(i=0; i<arrayLength; i++){
    wavelength=list[i];
    my_tone();
  }
}

void my_tone(){
    unsigned long cycle_number=timespan*285714/(wavelength+2); // forループで回す総数
    for(; cycle_number>0; cycle_number--){
      PORTB |=  _BV(SP); // LEDピンをHIGH
      for(j=wavelength; j>1; j--){}
      PORTB &= ~_BV(SP); // LEDピンをLOW
      for(j=wavelength; j>1; j--){}
    }
}

キャリブレーションとか言いながら、my_tone関数のcycle_numberのところで使っている285714とか2とかいう値は勘で決めています(汗

2. wavelengthと周波数のプロットを作成して近似直線を引きます。私はオシロスコープを使って1周期の長さを測定しました。下の図では両対数でグラフを書いています。下の絵はLibreOfficeで作成しました。

キャリブレーション.png

3. 近似直線(y=ax+b)の定数を求めます。
ここではLibreOfficeの関数を用いて以下のように求めました。
傾き a=LINEST($C$3:$C$9,$B3:$B9, 1, TRUE())
切片 b=INDEX(LINEST($C$3:$C$9,$B3:$B9, TRUE(), TRUE()), 1,2)
精度 R^2=INDEX(LINEST($C$3:$C$9,$B3:$B9, TRUE(), TRUE()), 3,1)
Attiny13A 4.8MHzとかなら -LOG(1-R^2) が6以上になれば合格といっていいでしょう。下記スクリーンショットはAttiny13A 4.8MHzでキャリブレーションした結果です。
スクリーンショット 2025-01-21 021802.png

4. あとは任意の周波数からwavelengthに代入すべき周波数が簡単に計算できます。
wavelength_float=(1000000/周波数-b)/a
このままだと小数点が付いてしまうので四捨五入して整数にします。
wavelength_int=int(wavelength_float+0.5)
最後に小数点を四捨五入した結果出力される予定の周波数を確認します。
出力予定の周波数=1000000/(wavelength_int*a+b)
スクリーンショット 2025-01-21 021949.png

周波数が高い領域では目的の周波数に近づけるのが難しくなるので、そういう時はマイコンの周波数を9.6MHzとかにあげると周波数を刻む間隔が小さくなるので精度が上げやすくなります。
私はキャリブレーションにオシロスコープを使いましたが、テスターの周波数測定機能を使っても同様のことはできると思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?