sbi()は1をセット(sはset)、cbi()は0を入れる(cはclear)。, bitSet()というarduino側で定義されてる関数も利用できます。というわけでレジスタの書き方はかなりたくさんの方法があります。これがTimerをわかりにくくしている理由のようにも思えますが。。, ちなみに以下のようにするとTimer関連情報をシリアルモニタから確認できます。 What is going on with this article? 1個目の引数でレジスタを指定して、2個目に変えたい項目を書きます。 Timer1を使用しています。, これは最初にTCCR1AとTCCR1Bを初期化して、TCCR1BのCS10とTIMSK1のTOIE1を1に変えています。TOIE1を1にするとカウンターがTOPにきた時(この例では16bitのTimer1を使っているので65535になった時)に割り込み処理を行います。この処理の中身はISR内に記入してあって、TOPになった時に何ミリ秒たったかをシリアルモニターに表示します。Normal modeはデフォルトのモードなのでレジスタを変更しなくても自動的にNormal modeになります。一番最初の方で紹介しましたが、16bitのTimer1では一回の周期が4.096msだったのでほぼ4ずつ数値が上がっていくのがわかるかと思います。そして、CS10についてですが、これは分周波(prescaler)を指定するものです。("<<"はC言語のビットシフトを表します。この計算方法については後ほど解説します。), CS10を1にする操作とは分周波を1にすることです(No prescaling)。この分周波とは16MHzの信号をその数値で割ってカウンターのTopに至るタイミングを遅くしたりできるのですが、これは実際に数値を変えてみるとよくわかります。例えば、CS10の行を消して以下のように書き直すと、, これはCS12を1にして分周波が256になったということです。数値のアップデートが遅くなっているのがわかります。1047という数値が出てきたかと思いますが、これは以下のような計算式で求めることができます。, 計算式としては1048.576ですがほぼ近似値が出ているのがわかります。では、カウンターの周期を1秒ちょうどで割り込みさせてみたいと思います。上の計算式から16bitの部分が62356ではなく62500となった時に1秒ちょうどになることが導けます。カウンターの値はTCNT1に指定することができるので、62356-62500=3036をカウンターの初期値として入れ、割り込むと同時にまたカウンターの初期値を毎回3036に変えています。, ではCTCモードをみていきます。Modeの表にある通り、CTCモードの時TOPはOCR1Aの値になります。このOCR1Aは先ほどのTCNT1のように直接数値を書き込むことができます。また同時にCTCモードではOCR1Bというcompare matchをもう一つ使ってOCR1AとOCR1B2つの割り込みを与えることができます。カウンターTCNT1がOCR1AあるいはOCR1Bの値とマッチした時に割り込みます(OCR -> Output Compare Register)。TIMSK1のOCE1AとOCIE1Bを1にすることでそれらの割り込みの許可を与えることができます。以下は1秒周期(OCR1A)とそこから0.5秒ずれた1秒周期(OCR1B)とで割り込み処理を行なっています。, Normal modeでもOCR1AとOCR1Bで割り込むことができますが、Normal Modeの場合TOPの値はTimerのbit数に依存し、Timer1なら65535、Timer0と2なら255でこの値は変えられません。一方でCTCの場合はTOPはOCR1Aで指定した値になり、そこでカウンターは0にリセットされます。つまり、Normal modeでは割り込みの頻度(周波数と表現していのかわからないので…)は変えられませんが、CTCでは変えることができます。イメージとして言うと、ノコギリみたいなカウンターの描く三角形の大きさがCTCだといくらでも小さくできて、リセットのタイミングを速められます。, Fast PWMでduty比を調整してPWMを送ることができます。また名前からもわかる通り、通常のdigitalWriteでは出せないような高い周波数のPWMを生成することも可能です。ここでレジスタTCCR1AのCOM1A1, COM1A0, COM1B1, COM1B0を操作して"non-inverting mode"か"inverting mode"かを選びます。non-invertingの時はOCR1A(OCR1B)とカウンターの値を比べた時に、カウンターの値の方が数値が小さい時にOC1A(OC1B)をHIGHに(データシートではHIGHになることをset、LOWになることをclearと表現しています)、大きい時にはLOWにします。"inverting mode"ではこれの真逆です。これは図を見ると一目瞭然だと思います。, イメージ内にも書いてありますが、PWMの周波数は以下のように求められます。bit数やprescalerはModeによって変更可能なので自分の好きな周波数を設定することができます。ちなみに以下の公式はnone-inverting, invertingどちらのモードでも同じ公式です。, !!周波数計算についての追記!! - ArduinoのTimerライブラリの紹介があります。Arduinoのタイマーライブラリ (英語ページ) - ArduinoのTimerに関する網羅的な記事です。初心者向けの優しい解説です。(英語) 「Skill Builder: Advanced Arduino Sound Synthesis」 - Arduinoでsine波を出す方法。 「Arduino DDS Sinewave Generator」. - ArduinoのTimerに関する網羅的な記事です。初心者向けの優しい解説です。(英語)

「Skill Builder: Advanced Arduino Sound Synthesis」 タイマオシレータicのts3004を使用して1秒毎にledを点灯します。今回のts3004は「3bitの分周値設定」と「rsetピンに任意の外部抵抗1本」を設置する事で周波数(3.3μ秒 ~ 233秒)をお手軽に設定可能です。 ArduinoのTimerに関する情報がまとまっていなかったのと、自分には複雑だったためかいつまんで重要そうなところだけまとめてみます。今後も加筆・修正していく予定です。後学のためにも間違いがありましたらご指摘いただけると幸いです。またこの記事において"arduino"は全てArduino Unoを指します。, 個人的にはATtiny85(Arduinoの超小型化版のようなもの。詳しくは->ArduinoユーザーのためのATTiny入門)で(擬似的な)サイン波を出すために必要でした。Arduinoではtone()やfor文を使って矩形波で音を出すことは可能ですが、サイン波など矩形波以外の信号を手軽に出すにはMozziなど外部のライブラリが必要でした。 |(OR)は -> いずれかのビットが 1 なら結果が 1 になるビット演算, これをふまえて以下のように書くと、レジスターTCCR1Bには0bxxxxx001が入リます。, これは最初TCCR1Bが"0bxxxxxxxx"の初期状態であり、ここに"b11111000"をANDで演算すると、"0bxxxxxx000"が出てきて、そこに"0b00000001"をORで演算すると、1の部分だけ取り出され、"0bxxxxxx001"が出てきます。 例えばTCCR0Aという一つのレジスタは8bitの数値で構成されているので、, という風に書いたとすると、1桁目の"WGM00"が"1"になっている状態となります。このレジスタの書き込み方については色々なやり方があるので後ほどまた説明します(実は上の書き方はベストではないのですが、わかりやすいためこう書いています。これでも機能します)。ちなみにこのようなTCCR0Aといったレジスタはデフォルトで定義されているものなので、当然のことながら変数宣言などいらず、上のようにarduinoのIDEに直接書けます。では組み合わせによってどのような機能を持ち得るか、代表的なものとしてModeというものがあります。, Modeを変更することによってカウンターを制御する方法を変えます。Timerは大まかにいうと4つのモードをもっています。, Normal modeとCTC(Clear Timer on Compare Match) modeはInterruput(割り込み処理)するためのもので、ある周期でなんらかの動作をさせたい時に使用し、Fast PWMとPWM Phase CorrectはPWMを生成するためのもので、カウンター+自分で指定した値の組み合わせからデューティー比、周波数を定義して電気信号を取り出したい時に使用します。割り込み処理をするタイミングはFlagを送るタイミングであり、これはISR()というすでにデフォルトで定義された関数を使って、そのフラグがきたタイミングにやってほしいことを自由に指定することができます。ちなみにarduinoはデフォルトでは割り込み処理が有効になっていますが、noInterrupts()を使って割り込み処理をプログラムの一部分で無効化することも可能です。それではそれらを踏まえた上で下がModeの構成表になります。, まずはWGM02とWGM01、WGM00に着目してください。これらの組み合わせがモードを決定していることがわかります。それぞれ0か1かしか数値を取らないので、2^3で8パターンのModeを選ぶことができます(とはいえmode4と6は"Reserved"になってるので実質は6パターンですが)。Timer/Counter Mode of OperationがModeの名前になり、Topとはカウンターの頂点がどのような数値になるかです。例えば"0xFF"であれば16進数で256なので、255がMax値になり、"OCRA(Output Compare Register A)"であればOCR0Aの値を定義してその値にカウンターが到達した時をTOPにして数値がリセットしますよ、ということです。例えばOCR0Aが200で"TOP"がOCR0Aなら、255に到達する前に200でカウンターは0にリセットされます(そしてまたカウントが繰り返されます)。Update of OCR0x atとありますが、これはOCR0AとOCR0Bの値をアップデートするタイミングを"Immediate"、つまりそのOCR0xの値にカウンターが到達した時にリセットするか、あるいは"TOP"とするとそのカウンターの頂点の時に、"BOTTOM"とすると信号の0の時にOCR0xがリセットされます。TOPとBOTTOMってタイミングほぼ一緒じゃないかと思われるかもしれませんが(というか自分は最初思いました)、ノコギリのように右上がりのカウントだけでなく、上がってまた下がっていく(カウントアップしてカウントダウンしていく)ような山のようなカウントの仕方も"PWM, Phase Correct Mode"ならできるので、TOPとBOTTOMでは意味が異なります。ちなみにOCR0xについては図を交えて後ほどまた説明します。TOV Flag Set onは、あるタイミングの時にフラグを送る、つまり今行動をおこしなさいという信号を送ります(Interrupt, いわゆる割り込み処理です)。"MAX"、"BOTTOM"、"TOP"はそのフラグを送るタイミングです。"MAX"は255にカウンターが到達した時で、"BOTTOM"はカウンターが0になった時、"TOP"はそのカウンターの周期の中で一番高い数値に到達した時です。これはOCR0Aに定義した値に依存します。, ここまでTimer0を例にとって話を進めてきましたが、実はTimer0が使われることはほとんどありません。。というのもTimer0はdelay()やmillis()、micros()などの機能を担っており、もっといってしまうとTimer0を使おうとするとエラーが出て書き込めません。。Timer0は先ほど挙げた時間を制御する機能を担っているので使えないということなのだと思いますが、ちょっとこの点については謎です。。ちなみにTimer1とTimer2では書き込めないといったことは起こりません(Timer2を使うとtone()は使えなくなりますが)。ただしTimer0とTimer2の構成はほぼ一緒なのと、Timer1でもビット数が16bitに変わるだけで基本的な考え方は全く同じです。, 上の図はTimer1のModeの構成表になります。WGMx0, WGMx1, WGMx2だけでなく、WGM13というものが増え、モードの数が増えたことがわかります。新しい情報もあるのですが、基本的なことはTimer0のモードと同じなので、ここではそこには触れずに具体例をあげてそれぞれのモードについて解説していきます。, まず以下のようなプログラムをarduinoに書き込んでください。