作成日:2013年1月3日
最終更新日: 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に記載されていたものを参考にした。

Sketch
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秒間隔の割り込みの優先度を高めたい。

Sketch
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");
}

このままの結果が以下。

Output
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()関数の出力は進捗しない。

上記スケッチの「割り込み優先度の設定」の部分のコメントアウトを外した(処理するようにした)結果が以下。

Output
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のハンドラが実行開始になっている)。

 



home | Elec top

inserted by FC2 system