LoginSignup
1
1

More than 1 year has passed since last update.

Arduino Nano Everyのタイマー割り込み機能について

Last updated at Posted at 2021-11-15

概要

Arduino Nano Everyのタイマー割り込み機能に関する覚書き。
原理とかどうでもいいからタイマー機能を使いたい!という方には下記リンク先のライブラリが便利。
https://github.com/Kees-van-der-Oord/Arduino-Nano-Every-Timer-Controller-B
(いつかこれも翻訳して整理したい)

Arduino Nano EveryにはType A(TCA)とType B(TCB)の2種類がある。
今回はTCBのch2を使用する。

ATmega4809をArduino Nano Everyとして使用する際、各タイマーは下記の機能に使われている。
TCAには比較チャンネルが3つあるので複数のPWM信号を出力できる(らしい。比較チャンネルを使ったことがなので理解が追い付いていない・・・)。
D3とD6は独立したタイマーを使用しているのでPWM周波数を変更しても他の機能に影響しない。

タイマー 標準ライブラリ PWM その他の機能
TCA D2,D5,D9,D10 TCB0~TCB3のクロック
TCB0 D6
TCB1 D3
TCB2
TCB3 delay()

※調査中のため抜け漏れの可能性あり。

なぜTCB2が使われていないのかな?と思って調べていたが、TCB2の出力ピンは2ピンあるが1つはPCとのシリアル通信のTXD、もう1つは未配線でどこにも繋がっていなかった。なので今回のような割込み処理に使うために残してあるチャンネルなのかと勝手に考えている。

実行環境

MCU:Arduino Nano Every (ATmega4809)
エディタ:Arduino IDE & VSCode

参考サイト

https://qiita.com/ShunHattori/items/1085de3d283b68683f9b
https://avr.jp/

参考資料

ATmega4809のデータシート
https://www.mouser.jp/datasheet/2/268/ATmega4808_09_DataSheet_DS40002173C-2237623.pdf
※avr.jpで日本語に翻訳されたものがダウンロード可。

サンプルコード

D2を100ms毎にHIGH/LOW切り替えするコード。
オシロがなければD13に設定して内蔵LEDで確認するのもOK。

int a = 1;

void setup() {
  pinMode(2, OUTPUT);

  TCB2.CCMP = 25000;  // TOP値の設定
  TCB2.CTRLB = (TCB2_CTRLB & 0b10101000) + 0b00000000;  //タイマーのGPIO出力ON、クロックソースを設定
  TCB2.CTRLA = (TCB2_CTRLA & 0b11111000) + 0b00000101;  //カウント周期を設定してカウントスタート
  TCB2.INTCTRL = 1;  //割り込み許可
}

void loop() {

}

ISR(TCB2_INT_vect) {
  TCB2.INTFLAGS = 1;  //割り込みフラグのクリア
  if (a == 1) {
    digitalWrite(2, HIGH);
  } else {
    digitalWrite(2, LOW);
  }
  a = 1 - a;
}

動作確認結果
タイマー割込み_動作確認.jpg

初期設定

初期化の手順

データシートのInitializationをGoogle翻訳
初期化
デフォルトでは、TCBは定期割り込みモードです。 使用を開始するには、次の手順に従ってください。
1. TOP値をCompare / Capture(TCBn.CCMP)レジスタに書き込みます。
2.オプション:コントロールB(TCBn.CTRLB)レジスタのCompare / Capture Output Enable(CCMPEN)ビットを「1」に書き込みます。 これにより、対応するピンで波形出力が使用可能になり、対応するPORT出力レジスタの値が上書きされます。
3.コントロールA(TCBn.CTRLA)レジスタのENABLEビットに「1」を書き込んでカウンタを有効にします。 カウンタは、コントロールA(TCBn.CTRLA)レジスタのクロック選択(CLKSEL)ビットフィールドのプリスケーラ設定に従って、クロックティックのカウントを開始します。
4.カウンタ値は、カウント(TCBn.CNT)レジスタから読み取ることができます。 ペリフェラルは、CNT値がTOPに達すると、CAPT割り込みとイベントを生成します。
4.1。 Compare / Captureレジスタが現在のCountレジスタよりも低い値に変更された場合、ペリフェラルはMAXにカウントされ、ラップアラウンドします。

TCBのレジスタ一覧

キャプチャ.PNG

今回の場合は、
1. TCB2.CCMPにTOP値を設定する。(16bitなので最大値は65535)
2. TCB2.CTRLAのCLKSELビットを設定してカウント周期を決める。(デフォルトではCLKTCAに設定されている)
3. TCB2.CTRLBのCNTMODEビットに0を書き込んで定期割り込みモードに設定する。(デフォルトではPWM出力モードに設定されている)
4. TCB2.CTRLAのENABLEビットに1を書き込んでタイマーを有効化・カウントを開始する。
5. TCB2.INTCTRLのCAPTビットに1を書き込んで割込み処理を有効にする。
6. ISR(TCB2_INT_vect)に割込み発生時の処理を書く。処理の中でTCB2.INTFLAGSのCAPTビットに1を書き込んで次の割込みが入るようにしておく。

※TOP値 →カウンタがTOP値になったら割り込み発生
※割り込み周期 = カウント周期 x TOP値(サンプルコード: 4us x 25000カウント = 100ms)
※TCB2.INTFLAGSをいつ処理するかは用途に応じて変える。個人的にはタイマーの目的を考えて最初に書くことが多い。

メリット デメリット
処理の最初に実行 割込み処理が必ず
設定した周期で実行される
割込み処理が終わっていなくても
時間が来たら次の処理が実行される
→バグ発生
処理の最後に実行 割込み処理が最後まで
確実に実行される
次の割込みの時間になっても処理中なら
次の割込み処理は無視される
→割込み周期が不安定になる

CLKSELの設定値

設定値 設定名 カウント周期 設定可能な割り込み頻度
00 CLKDIV1 62.5ns 62.5ns ~ 約4.1ms
01 CLKDIV2 125ns 125ns ~ 約8.2ms
10 CLKTCA 4us 4us ~ 262.14ms

※CLK_PERはデフォルト設定(62.5ns・16MHz)と仮定してカウント周期を計算。
※TCA0はデフォルト設定のDIV64(4us・250kHz)と仮定してカウント周期を計算。

CLKDIV1に設定して16回割り込みに入るとちょうど1usなので、これを利用すると使いやすいライブラリが作れそう。

PWM出力に設定してクロックの確認

PWMの設定については下記。
正確な周波数を測定するため、CCMPH = 127, CCMPL = 255 で出力してn=255の平均値と考える。

キャプチャ2.PNG

下写真のようにPC0の配線を引き出してオシロに接続
DSC_0147.JPG

CLKDIV1
PWM周期 = 16Mhz / 255カウント ≒ 62.745kHz
タイマー割込み_動作確認2.jpg

CLKDIV2
PWM周期 = (16Mhz / 2) / 255カウント ≒ 31.373kHz
タイマー割込み_動作確認.jpg

CLKTCA
PWM周期 = (16Mhz / 64) / 255カウント ≒ 980.392Hz
タイマー割込み_動作確認.jpg

CNTMODEの設定値

設定一覧

設定値 設定名 機能の日本語訳
000 INT 定期割り込みモード
001 TIMEOUT タイムアウトチェックモード
010 CAPT インプットキャプチャ
011 FRQ インプットキャプチャ・周波数測定モード
100 PW インプットキャプチャ・パルス幅測定モード
101 FRQPW インプットキャプチャ・周波数/パルス幅測定モード
110 SINGLE シングルショットモード
111 PWM8 8bit PWMモード

定期割り込みモード

キャプチャ.PNG
カウント=TOP値になった時に割り込み処理を実行するモード。
一定周期で何か処理したいときに使う。

タイムアウトチェックモード

キャプチャ.PNG
対応しているピンに別のICのデジタル信号を接続し、立上りを検出してカウントを開始、カウント=TOP値になった時に割り込み処理を実行するモード。
一定時間応答がなかった時に何か処理をしたいときに使う。

インプットキャプチャ

キャプチャ.PNG
対応しているピンに別のICのデジタル信号を接続し、立下りを検出したときに割り込み処理を実行するモード。
外部割込みと異なる点は、割込みが発生したときのCNT値がCCMPに格納される点。
カウントは割込み処理中も常に回っており、外部入力によるカウントリセットはしない。
カウントをリセットしたいときはCNTレジスタに0を書き込む。

周波数測定モード

キャプチャ.PNG
対応しているピンに別のICのデジタル信号を接続し、立上りを検出したときに割り込み処理を実行するモード。
インプットキャプチャと異なる点は、立上りで割込みが発生することと、割込みが発生するとCNTを0にリセットするところ。
CNTを0にする前にCNT値をCCMPに格納するので、立上りから次の立上りまでの経過時間が測定できる。

パルス幅測定モード

キャプチャ.PNG
対応しているピンに別のICのデジタル信号を接続し、立下りを検出したときに割り込み処理を実行するモード。
インプットキャプチャと異なる点は、立上りを検出したときにCNTを0にリセットするところ。
割り込み発生時にCNT値をCCMPに格納するので、立上りから立下りまで(信号がHIGHになっている時間)の経過時間が測定できる。

周波数/パルス幅測定モード

キャプチャ.PNG
対応しているピンに別のICのデジタル信号を接続し、立上りを検出したときに割り込み処理を実行するモード。
インプットキャプチャと異なる点は、立上りで割込みが発生すること、割込みが発生するとカウントストップすること、カウントの自動リセットがあること。
カウントの自動リセットはCCMP値を他の変数等に代入した後の立上りで実行される。(CCMPを読むまでの立上りは無視される)
割り込み処理でCNT値とCCMP値を読むことでHIGH時間と周期が同時に測定できる。(DUTY測定に便利)

シングルショットモード

image.png
使ったことがないので説明は省略します・・・
何かしらのイベントが発生してから一定時間、対応したピンからHIGHを出力するためのモードと思われる。たぶん。

8bit PWMモード

image.png
対応したピンからPWM信号を出力するモード。このモードのみ、CCMPの上位8bitをCCMPH、CCMPの下位8bitをCCMPLと呼ぶ。
CCMPHはPWMのDUTYを、CCMPLはTOP値(=PWM周波数)を設定するために使う。
TOP値が8bitになるためカウントの最大値が255となり、設定できる周波数の選択幅が狭くなるので注意。
またCNT = TOP値となった時に割り込みが発生するので、同じチャンネルでPWM出力と割込み処理を同時に実行も可能。(割込み処理を重い内容にするとPWMに影響が出るかも)

割り込み処理

AVRではISR(割込みベクタ) という関数を定義して割り込み処理を記述する。
ATmega4809でのTCB2の割込みベクタはTCB2_INT_vect の名前で定義されている。
実際にどのように書くかはサンプルコード参照。
他の割込み処理の名前を調べるときは、vscodeの機能を使ってTCB2_INT_vect の定義にジャンプし、その周辺を探るとラク。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1