矩形波だけのファンクションジェネレーターの波形生成はTimer/CounterのPWMでやる方向で考えた。
単にTimerで割込みをかけてCPUで処理すると割込みのオーバーヘッドが大きいようだ
レジスタの退避で数十クロック使ってしまうらしいです
参考:「クレア工房」さん(
http://www.clarestudio.org/elec/avr/mcu-interrupt.html)
PWMなら周辺機能のハードウェアが勝手に仕事をしてくれるので(たぶん)任せることにした
音の生成には、今までのDACしか使ったことがないので多少ハマりつつPWMの実験してみた
Timer1
ATMega328PのTimer/Counterには、8bitのTimer0、Timer2、16bitのTimer1がある
ファンクションジェネレーターで生成する波形の周波数はできるだけ下から上まで取りつつ、分解能も細かくしたいので16bitのTimer1を使ったほうがよさそうだ
Timer1の動作モードは
- Normal Mode
- Fast PWM Mode
- Phase Correct PWM Mode
- Phase and Frequency Correct PWM Mode
とあって、どう違うのかもはっきりわからなくて
いきなりげんなり(^q^;
唯一持ってるAVRの本の
「AVRマイコン・リファレンス・ブック」
と、DATA SHEET(本家版と翻訳版(
www.avr.jp/user/DS/PDF/mega88A.pdf))にランダムアクセスしながらテストプログラムを書いてみた
要件としては
- 周波数可変
- デューティー比可変
だ
ブレッドボードの配線
<PWM_Test.c>
 #include <avr/io.h>  
 #include <avr/interrupt.h>  
 #include <stdint.h>  
 #define F_CPU     16000000UL  
 #include <util/delay.h>  
 void timer1_init_FastPWM(uint16_t cycle, uint16_t duty)  
 {  
      // Output Compare Register  
      OCR1A = cycle;  
      OCR1B = duty;  
      // Initialize Counter  
      TCNT1 = 0;  
      // Phase and Frequency Correct PWM Mode, TOP = OCR1A  
      TCCR1B |= (1 << WGM13) | (0 << WGM12);  
      TCCR1A |= (0 << WGM11) | (1 << WGM10);  
      // Compare Output OC1A, OC1B  
      TCCR1A |= (0 << COM1A1) | (1 << COM1A0);  
      TCCR1A |= (1 << COM1B1) | (0 << COM1B0);  
      // Timer1 Start, Set Prescaler to 8  
      TCCR1B |= (1 << CS11);  
 }  
 int main(void)  
 {  
      DDRB = 0xFF; // PB1(OC1A) 出力  
      timer1_init_FastPWM(500, 375);  
      sei();  
   while(1)  
   {  
     //PORTB ^= 0x06;  
     //_delay_ms(50);  
   }  
 }  
動作モードを指定するWGMは簡単に変更できるように(冗長ですが)しておいた
Timer1のPWMはOC1A,OC1B(328PだとPin15とPin16)から出力され、これも簡単に設定が変えられるようにしていおいた
試行錯誤しまくったので、最終的にこういうコードになっただけですが(^q^;
Fast PWM
Mode15 WGM[13:10] 0b1111
PWMで周波数、デューティー比可変できるもので一番基本っぽいので最初はこれからやってみた。
周波数をOCR1A、デューティー比をOCR1Bで指定。
OC1A, OC1Bの出力設定はよくわからないので適当に試してみたが波形が出力されなかった(全パターン試したわけではないです)
動作モードの指定は
 // Fast PWM Mode, TOP = OCR1A
 TCCR1B |= (1 << WGM13) | (1 << WGM12);
 TCCR1A |= (1 << WGM11) | (1 << WGM10);
PWM出力の指定は
 // Compare Output OC1A, OC1B
 TCCR1A |= (1 << COM1A1) | (0 << COM1A0);
 TCCR1A |= (1 << COM1B1) | (0 << COM1B0);
なんかおかしいのかな(@@;
PWM, Phase and Frequency Correct
Mode3 WGM[13:10] 0b0011
「ELECTRO SCHEMATIC」さんの「AVR PWM Pulse Width Modulation – Tutorial #12」
(
http://www.electroschematics.com/9941/avr-pwm/)にデューティー比をいじるソースコードが載っていたので
コピペで動かしたらちゃんと動いた
が、これは
「PWM, Phase Correct, 10-bit」というモードでデューティー比は可変だが、周波数は固定(プリスケーラーで大まかには設定できるかな?)
Mode9 WGM[13:10] 0b1001
TOP値をOCR1Aで指定するモードの中で、
「Fast PWM Mode」はあきらめて
「PWM, Phase and Frequency Correct」で動かしたら動いた(^q^/
波形をオシロで観測(上記ソースコードのパラメータで動作させています)
赤色はOC1A、黄色はOC2A OC1Bの出力
OC1Aは、
TCCR1A |= (0 << COM1A1) | (1 << COM1A0);
 にしているのでコンペア・マッチでトグル出力
※この辺も、もうどういう動作を指定しているのか確信が持てません(^q^;
オシロの計測値
Dutyは75%になっているのでプログラムで指定した通りのデューティー比だ(cycle:500, duty:375)
出力周波数は、Duty比50%の赤色の方で見て1kHz
周波数は計算式はもうちょっとまじめにDATASHEETを読まないとわからないが、まあcycleが1あたり2μ秒だと思う
ということは
cycle = 1 で500kHz
cycle = UINT16_MAX(65536) でだいたい7.63Hz
がんばれば10Hz~500kHz程度の矩形波を生成できそうだ
周波数やデューティー比をLCDに表示しようと思うと
プログラムの計算に小数の乗算が出てきてCPUのリソースを食いそうなので
PWM波形生成がほんとにCPUと独立してるのかという点が気になるが。。。
今後AVRを使うことはあんまりないかもしれないので製作途中で問題が出てきたらまた調べたいと思います
矩形波のノイズ
オシロの波形を大まかに見ても矩形波にかなりノイズが乗っている(@@;
線が太くなっているところを拡大
だいたい32MHzなのでクロック由来のデジタル・ノイズのような気がする
前回の「測定器のまとめ」でオシロのアナログ帯域は20MHzで十分と書いたので、オシロの設定で20MHzの帯域制限をかけてみた
表示波形はフルバンド(帯域制限なし:100MHz)の時と大差ないです
↓フルバンドで計測
一次CR LPFをとりあえずかけてみた
「OKAWA Electric Design」さんのフィルタ計算ツール(
http://sim.okawa-denshi.jp/CRlowkeisan.htm)でだいたいfc = 1MHzを計算して実験
→
fc = 1 / (2 * π * √(C * R)) fc = 1 / (2 * π * C * R)を変形してシミュレーションというのもすでにめんどくさくなってる(^q^;
R:100Ω C:100pF fc=だいたい16MHz
赤色がLPFを通した後、黄色がLPFを通す前
R:100Ω C:1000pF fc=だいたい1.6MHz
R:330Ω C:1000pF fc=だいたい480kHz
1次LPFだと多少カットオフ周波数をいじってもノイズの減衰は大差ない
過渡現象
R:100Ω C:100pF fc=だいたい16MHz
赤色がLPFを通した後、黄色がLPFを通す前
R:100Ω C:1000pF fc=だいたい1.6MHz
R:330Ω C:1000pF fc=だいたい480kHz
過渡現象はかなりはっきり違いが現れる(^q^;
矩形波に集中してるのに波形がなまってしまってはどうしようもないので弱めの2次LPFぐらい入れられればいいのかな
ケースの本体側に別基板を入れて、出力部だけ分けられるかどうか妄想中