最終更新日: 2013年1月21日
Arduino DUEのタイマー割り込みメモ
DUEでタイマー割り込みを使う時のメモ
注:この情報はドキュメント作成日におけるIDEの最新版であるVer1.5.1を元に作成しています。このIDEはβ版のため、今後変更される可能性があります。
基本
SAM3X8Eには3組のタイマー・カウンターのブロック(TC0,TC1,TC2)があり、各ブロックには32bitのタイマーが3チャンネルある。つまり合計9本のタイマーがある。
タイマー割り込みには、割り込みの名称や割り込みハンドラの名称はTC0のch0ならTC0_Handler、TC1のch1ならTC4_Handlerというように、チャンネル毎にTC0〜TC8にナンバリングされた名称が使われる。これと3組のカウンターであるTC0,TC1,TC2とは別なので混同しないよう注意が必要(ここ、わかりにくい)。
割り込みハンドラは割り込みの種類に応じてATMELで決められた名称の割り込みハンドラの関数(例えばTC0_Handler()とか)を記載するだけで良い。
DUEでは標準ではTC1のブロックは使用していないらしい(TC0,TC2はPWM出力に使用されている)。なのでタイマー割り込みにはTC1のブロックを使うと良いかと思う。
※「ブロック」という呼称は勝手に用いたもので、データシートでこのように呼ばれているわけではない。データシート上では「3個のタイマー、3個のチャンネル」である。
基本はこんなかんじ。なおstartTimer関数はArduino Forumに記載されていたものを参考にした。
void setup() { startTimer(TC1, 0, TC3_IRQn, 1000); } void loop() { } void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t mSec) { pmc_enable_periph_clk((uint32_t)irq); TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); uint32_t rc = (VARIANT_MCK/2/1000)*mSec; TC_SetRC(tc, channel, rc); TC_Start(tc, channel); tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS; tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS; NVIC_EnableIRQ(irq); } void TC3_Handler() { TC_GetStatus(TC1, 0); // 割り込み発生時に実行する部分 }
pmc_enable_periph_clk()で使用するペリフェラルを使用可能にする。このパラメーターは厳密にはペリフェラルIDだが、IRQ番号と同じなのでそのまま渡している。
TC_Configureで動作モード(カウンタがRCと同一になったら割り込み&リセット)やプリスケーラーをセット、TC_SetRCでRCレジスタに設定する。その後タイマーをスタートし、割り込みを有効にする。
これで、上記例では1000mSec間隔でTC3_Handler() が呼ばれる。
多重割り込みとSysTick割り込みの制御
SAMではほとんどの割り込み優先度が自由に設定できる。デフォルトでは優先度の設定がすべて「0」に設定されるので、基本的には同一レベル。ということで多重割り込みを行うためには優先度を設定する必要がある。
また、DUEではdelay()やmillis()関数等の動作にはARMのSysTickタイマーが使用されている。標準ではSysTick割り込みは15(最低)に設定されるので、そのままでは割り込みハンドラ内ではdelay()関数は使えなかったり、millis()関数の結果は割り込みハンドラ実行中は進捗しない。この、割り込み優先度が最低に設定されるのはArduinoの仕様というより、CMSISのライブラリであるSysTick_Configを使うとそのように設定されてしまう。
割り込みを使用しつつmillis()関数の結果を信頼したい場合には、SysTick割り込みの優先度を他の割り込みハンドラよりも上げる必要がある。
下記は、3チャンネルのタイマーを使い、1,4,8秒間隔に多重割り込みを発生させる例。意図としては1秒間隔の割り込みの優先度を高めたい。
char buff[255]; void setup() { Serial.begin(57600); int i; int level; /* 割り込み優先度の設定 NVIC_SetPriority((IRQn_Type)SysTick_IRQn,0); NVIC_SetPriority((IRQn_Type)TC3_IRQn,1); NVIC_SetPriority((IRQn_Type)TC4_IRQn,2); NVIC_SetPriority((IRQn_Type)TC5_IRQn,3); */ startTimer(TC1, 0, TC3_IRQn, 1000); startTimer(TC1, 1, TC4_IRQn, 4000); startTimer(TC1, 2, TC5_IRQn, 8000); } void loop() { } void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t mSec) { pmc_enable_periph_clk((uint32_t)irq); TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); uint32_t rc = (VARIANT_MCK/2/1000)*mSec; TC_SetRC(tc, channel, rc); TC_Start(tc, channel); tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS; tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS; NVIC_EnableIRQ(irq); } void printWithMillis(char *s) { sprintf(buff,"%06d %s",millis(),s); Serial.println(buff); } void TC3_Handler() { TC_GetStatus(TC1, 0); printWithMillis("TC3"); } void TC4_Handler() { TC_GetStatus(TC1, 1); printWithMillis(" TC4 IN"); // 意味は無いけど時間がかかる処理 volatile float x,y; for (x=0; x< 4000; x += 0.1) { y = sin(x); } printWithMillis(" TC4 OUT"); } void TC5_Handler() { TC_GetStatus(TC1, 2); printWithMillis(" TC5 IN"); // 意味は無いけど時間がかかる処理 volatile float x,y; for (x=0; x< 7000; x += 0.1) { y = sin(x); } printWithMillis(" TC5 OUT"); }
このままの結果が以下。
001002 TC3 002002 TC3 003002 TC3 004002 TC3 004002 TC4IN 004002 TC4 OUT 004002 TC3 004801 TC3 005801 TC3 005801 TC4 IN 005801 TC4 OUT 005801 TC3 005801 TC5 IN 005801 TC5 OUT 005801 TC3
このように、TC4とTC5のハンドラ実行中にはTC3の割り込みが発生しない(優先順位が同じなので)。さらに割り込みハンドラ実行中にはmillis()関数の出力は進捗しない。
上記スケッチの「割り込み優先度の設定」の部分のコメントアウトを外した(処理するようにした)結果が以下。
001002 TC3 002002 TC3 003002 TC3 004002 TC3 004003 TC4 IN 005002 TC3 006002 TC3 006205 TC4 OUT 007002 TC3 008002 TC3 008003 TC4 IN 009002 TC3 010002 TC3 010205 TC4 OUT 010207 TC5 IN 011002 TC3 012002 TC3 012003 TC4 IN 013002 TC3 014002 TC3 014205 TC4 OUT 015002 TC3 016002 TC3 016003 TC4 IN 017002 TC3 018002 TC3 018205 TC4 OUT 018487 TC5 OUT 018490 TC5 IN 019002 TC3 020002 TC3
まずTC3の割り込みが、TC4,5のハンドラ実行中であっても1秒間隔に実行されていることがわかる。TC4の2回目の実行時にTC5の割り込みが来るはずだが、優先順位が低いので後回しにされている(TC4のハンドラ実行完了後にTC5のハンドラが実行開始になっている)。