みなさん,こんにちは
おかしょです.
ロボットの制御をするときは一定の周期で入力を与えることが重要です.
一定の周期でないと,制御入力の周波数が変わってしまいロボットが予期せぬ動きをする可能性があるからです.
今回はArduinoで制御入力を一定の時間間隔で与えるために便利なタイマーという割り込み機能を行います.
この記事を読むと以下のようなことがわかる・できるようになります.
- タイマー割り込みとは
- Arduinoでタイマーを使うプログラム
この記事を読む前に
Arduinoでタイマーを使いたい方の多くはロボットを作ろうとしている方だと思います.
以下の記事で,ロボットを作るために必要な手順を解説しているので参考にしてみてください.
タイマー割り込みとは
そもそもタイマー割り込みとは何でしょうか.
Arduinoのプログラムはsetup関数とloop関数に書き込みます.loop関数は繰り返し実行されるので,一定の時間間隔でプログラムを実行したい場合はdelay関数などを使って一定の周期でプログラムが実行されるように調整するかもしれません.
しかし,このようにdelayを使ったやり方は正確な周期を生み出すことができません.delay関数は任意の時間プログラムの実行を遅らせることができる関数ですが,実際のプログラムがどのくらいの時間が必要なのかがわからないからです.
以下の図を見てください.
左の図はloop関数のみを取り上げたプログラムになります.
プログラム中の青い部分は,処理が終わるまでに決まった時間がかかるので問題ありませんが,「プログラム」と書かれた部分はどのくらいの時間がかかるのかがわかりません.また,1ループごとに電気的なノイズが加わるためdelay関数で調整しても一定の周期で実行することはできません.
そのようなときに便利なのがタイマーです.
タイマーはloop関数とは関係なく,一定の時間間隔で実行できます.そのため,ロボットの制御入力を行う際に非常に便利な機能です.ほかにもセンサーの測定間隔も一定の周期で行われるので,timerはそのような場合でも非常に便利に働いてくれます.
仕組みとしては非常に単純で,自動で実行時間をカウントしていき,ある数値に到達したら処理を行います.
タイマーの種類
1つのマイコンにタイマーは1つではなく,複数搭載されていることが多いです.Arduino Unoもタイマーは1つだけではなく3つ(Timer0,Timer1,Timer2)用意されています.
タイマーにはPWMピンが割り当てられていて,それを使って周期的に制御をすることが可能になります.
\begin{array}{c|c|c}
\ Timer\ & \ \ \ pin\ \ \ & \ \ 最大値\ \ \\
\hline
Timer0 & 5, 6 & 8 bit\\
\hline
Timer1 & 9, 10 & 16 bit\\
\hline
Timer2 & 3, 11 & 8 bit\\
\end{array}
表の最大値というのはカウンタの最大値を表します.つまり,この値によって割り込む間隔の上限が決まります.
具体的にどういうことかというと,Arduino Unoの場合は16MHzで動作します.1秒間で16M回動作するということです.8bitのタイマーの場合は最大で
$$ \frac{1}{16M}\times 255(8bit)=1.59\times 10^{-5}秒 $$
の間隔で割り込みをすることができます.
タイマーの種類は3種類あると言いましたが,Timer0はdelay関数が割り当てられているため割り込みとしては使用できないみたいです.
ここからはTimer1を例にして設定方法などを解説していきます.
タイマーの設定方法
次にタイマーの設定方法について説明します.
タイマーの設定にはモードとステップカウント速度,カウント値の3つの設定があります.それぞれ順番に解説します.
モード
タイマーのモードはNormalとCTC,Fast PWM,PWM Phase Correctの4種類があります.
NormalとCTCは一定の時間間隔で動作させたいときに使用します.両者の違いは,CTCの場合はカウントの値を直接指定できるところにあります.
FastPWMとPWM Phase Correctは一定の時間間隔でPWM信号を送信することができます.両者の違いはカウントの仕方にあります.
FastPWMはノコギリ波でカウントするのに対して,PWM Phase Correctは三角波でカウントをします.
つまり,Fast PWMは常にカウントアップで計算するのに対して,PWM Phase Correctはカウントアップしたらカウントダウンをします.
モードの設定には各タイマーに設定されているレジスターを使用します.レジスターは複数用意されていて,その中でモードの設定ができるレジスターはTCCRxAとTCCRxBです.xには0,1,2が入ります.各レジスターは以下のように配置されています.
TCCR1A
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
7\hspace{ 50pt }& 6\hspace{ 50pt}&5\hspace{ 50pt}& 4\hspace{ 50pt}& 3\hspace{ 50pt}& 2\hspace{ 50pt}& 1\hspace{ 50pt}& 0\hspace{ 50pt}\\
\hline
COM1A1& COM1A0&COM1B1& COM1B0&-& -& WGM11& WGM10\\
\hline
\end{array}
TCCR1B
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
7\hspace{ 50pt }& 6\hspace{ 50pt}&5\hspace{ 50pt}& 4\hspace{ 50pt}& 3\hspace{ 50pt}& 2\hspace{ 50pt}& 1\hspace{ 50pt}& 0\hspace{ 50pt}\\
\hline
ICNC1& ICES1&-& WGM13& WGM12& CS12& CS11& CS10\\
\hline
\end{array}
モードの設定はWGM13, WGM12,WGM11, WGM10の組み合わせによって,以下のように設定できます.
\begin{array}{|c|c|c|c|c|c|}
\hline
0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&Normal\\
\hline
1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&PWM Phase Correct\\
\hline
2\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&PWM Phase Correct\\
\hline
3\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&PWM Phase Correct\\
\hline
4\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&CTC\\
\hline
5\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&Fast PWM\\
\hline
6\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&Fast PWM\\
\hline
7\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&Fast PWM\\
\hline
8\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&PWM Phase and Frequwncy\\
\hline
9\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&PWM Phase and Frequwncy\\
\hline
10\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&PWM Phase Correct\\
\hline
11\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&PWM Phase Correct\\
\hline
12\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&CTC\\
\hline
13\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&Reserved\\
\hline
14\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&Fast PWM\\
\hline
15\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&Fast PWM\\
\hline
\end{array}
ステップカウント速度
ここでは,1秒あたりにカウントされる回数を設定します.
Arduino Uno動作は16MHzだということを先程述べました.16MHzで動作するということは1秒あたりに16Mもカウントされることになります.これでは値が大きすぎて,わかりずらいです.
そこで,1秒あたりにカウントされる回数を調整します.これを分周比と言いますこれはTCCR1BレジスターのCS12,CS11,CS10で設定できます.
\begin{array}{|c|c|c|c|c|}
\hline
0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&Stop\\
\hline
1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&1\\
\hline
2\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&8\\
\hline
3\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&64\\
\hline
4\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 0\hspace{ 20pt }&256\\
\hline
5\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }& 1\hspace{ 20pt }&1024\\
\hline
6\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 0\hspace{ 20pt }&-\\
\hline
7\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }& 1\hspace{ 20pt }&-\\
\hline
\end{array}
例えば,分周比を8に設定した場合
$$ 16M\times \frac{1}{8}= 2M$$
となり,1秒間に2M回カウントされることになります.
この設定を行わなかった場合は16MHzのままに設定されます.
カウント値の設定
タイマー割り込みはカウント値がある値になった時に発生します.ここでは,カウント値の設定を行います.
これを設定するためのレジスターはOCR1AとOCR1Bというレジスターです.このレジスターにカウント値を直接入力することで設定できます.
ここで,カウントは0からはじまるので10カウントごとに割り込ませたい場合は\(10-1=9\)と設定することに注意してください.
タイマーに設定を書きこむ方法
Timer1に書き込みたい場合は,以下のようにすることで書き込めます.
TIMSK1 |= (1 << TOIE1);
Timer2の場合は以下のようにします.
TIMSK2 |= (1 << OCIE2A);
割り込み処理の書き方
タイマーで割り込んだ時の処理は以下のように書きます.
Timer1の場合は
ISR (TIMER1_COMPA_vect) {
処理内容
}
ISR(TIMER1_COMPB_vect) {
処理内容
}
Timer2の場合は
ISR (TIMER2_COMPA_vect) {
処理内容
}
とすることで,割り込みが可能となります.
タイマー設定サンプル
最後に,サンプルプログラムとしてTimer1の割り込みを10Hzと20Hzで行う設定方法を以下に示します.
void setup()
{
TCCR1A = 0; // 初期化
TCCR1B = 0; // 初期化
OCR1A = 6250; // 10Hz
OCR1B = 3125; // 20Hz
TCCR1B |= (1 << CS12) | (1 << WGM12); // 分周比256, CTCモードに設定
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B);
}
ISR (TIMER1_COMPA_vect) {
処理内容;
}
ISR(TIMER1_COMPB_vect) {
処理内容;
}
まとめ
この記事ではArduino Unoでタイマー割り込みを使う方法を解説しました.
Timer2のレジスターの配置などはArduinoのCPUであるATmegaのデータシートを参照して,同じように設定してみてください.
ここで公開したプログラムを使えば,タイマー割り込みが簡単に使えると思うので参考にしてください.
続けて読む
このタイマー機能を使って姿勢角の取得を以下の記事では行っています.
上のプログラムの具体的な使用例を知りたい方は読んでみてください.
Twitterでは記事の更新情報や活動の進捗などをつぶやいているので気が向いたらフォローしてください.
それでは最後まで読んでいただきありがとうございました.
コメント