Arduino 周波数/周期カウンタ (10)

前回の最後の図を下に再掲します。 このグラフは、

  • ある特定の ICR1 の値 (となるような CAPT タイミングを発生する OCR1B の設定) で、
  • CAPT 割り込みサービス・ルーチン内で TOV1 フラグの状態を観測し、記録するのを
  • 10000 回繰り返すことにより発生頻度を求める

ことを、ICR1 の値を変えながら、繰り返して得られた結果を、ICR1 の値を横軸、TOV1 の発生頻度を縦軸として、プロットしたものです。

グラフのプロットの都合上、ICR1 の値は「符号付き 16 ビット整数」として扱っているので、0xFFFF 以下の 値は「マイナス」として表示されています。
赤い線は、サンプル・スケッチとして示したような、周期測定終了フラグが立っていなければ SLEEP 状態に移行して割り込みを待つ場合の結果です。
一方、青い線は周期測定終了を通常のフラグ・チェック・ループで待つ (SLEEP 状態には入らない) 場合の結果です。
まず、赤い線の「SLEEP あり」の場合を説明します。
SLEEP ありの方は、ICR1 の値が「6」までは TOV1 を観測する割合が完全に 100 % で、「7」以上では TOV1 を観測する割合が完全に 0 % となっています。
これは、割り込みが入って SLEEP 状態から割り込み応答シーケンスに移行するまでに要する時間が 6 クロックであることに関係しています。
ATmega のスリープ動作には 6 種類ありますが、ここでは最も眠りの浅い「アイドル」動作を使っています。
アイドル動作では、CPU は停止しますが、すべての I/O は動作を続けます。 タイマ 1 が停止しない状態はこのアイドル動作だけなので、他の選択の余地はありません。
データシートでは、「スリープ動作」からの「復帰時間」については、メインクロック発振回路を停止するモードでは電源投入時と同等の時間がかかることと、「拡張スタンバイ」からは 6 クロックで「起動」することだけが書かれており、「アイドル」動作からの起動クロック数については明記がありません。
しかし、クロック発振回路を停止させるモードは別として、I/O へのクロックを停止する複数のモードでは、停止する I/O の範囲に差があるだけで、CPU が「復帰」するメカニズムにはモードごとの差はないと考えられるので、「アイドル」動作でも 6 クロックで割り込み応答シーケンスへ移行するものと解釈できます。
この 6 クロック間は、まだ割り込み応答シーケンスではないので、OVF の割り込み要求により「アイドル・スリープ」から目覚めた後、この 6 クロック内に CAPT 割り込みに「追い越される」可能性があり、追い越された場合には CAPT 割り込みの方が優先度は高いので、OVF 割り込みは 100 % 後回しになってしまいます。
6 クロック以上経過し、割り込み応答シーケンスに入ってからは、順番が逆転する可能性は 0 % です。
これが、ICR1 の値が「6」までは TOV1 を観測する頻度が 100 % となり、「7」以上では 0 % となる理由です。
ICR1 の値が「0」以上で、なおかつ TOV1 が立っている場合は、未処理の OVF 割り込みがあることを示し、ソフトウェア拡張したカウンタ上位桁のインクリメントが済んでいませんから、補正の必要があることを意味しています。
測定対象の波形のエッジ位置はランダムであると仮定すると、65536 サイクルで一回りするカウンタの、6 カウント分の区間では補正なしでは「エラー」が発生するわけですから、開始エッジと終了エッジの 2 回で誤差が発生する可能性を考えると、エラーの確率は、

\qquad\qquad 2 \times (6/65536) = 12/65536

となります。
12 月 13 日付けの記事 (http://d.hatena.ne.jp/pcm1723/20091213) で補正なしでは約 1/5000 の確率でエラーが生じると書いたのは、これを意味していました。
次に、青い線の「SLEEP なし」の場合を説明します。
ICR1 の値が 0xFFFF (-1) では頻度は 100 % になっていますが、0 から 4 までは段階的に頻度が下がってきています。
まず、OVF と CAPT との関係を示す最初のタイムチャートのところで述べた通り、ICR1 = 0xFFFF では OVF と CAPT は「同時」に発生しており、優先度の高い CAPT が必ず勝つので 100 % OVF が後回しになります。
ICR1 = 0x0000 では、実行中の命令が 1 サイクル命令であれば OVF → CAPT の順に割り込みがサービスされ、CAPT 割り込みルーチン内では TOV1 が立っているのが観測されることはありません。
ところが、OVF 割り込み要求が生じたときに実行中の命令が 2 サイクル (以上の) 命令の場合は CAPT に「追い越されて」CAPT → OVF の順に割り込みがサービスされることは、以前に述べました。
つまり、ICR1 = 0x0000 で TOV1 が観測されない約 38 % は、OVF 割り込み要求発生時に実行していた命令が (複数) サイクル命令の最後のサイクルであった確率ということになります。
また、TOV1 が観測された約 62 % は、その時点で、

  • 2 サイクル命令の 1 サイクル目
  • 3 サイクル命令の 1 〜 2 サイクル目
  • 4 サイクル命令の 1 〜 3 サイクル目

のいずれかを実行中であったことを示しています。
実際のフラグ待ちループは、

  • 1 サイクル命令 2 個
  • 2 サイクル命令 3 個
  • 4 サイクル命令 2 個

で構成されており、それから期待される確率を計算すると約 56 % となり、測定値と大きくは違いません。
ICR1 = 0x0001 では測定値は約 19 % で、

  • 4 サイクル命令の 1 〜 2 サイクル目

のいずれかを実行中であったことを示しています。 (3 サイクル命令は使われていないので省略)
期待値は約 25 % で、あまり良くは合っていませんが、大きくも違いません。
ICR1 = 0x0002 では測定値は約 11 % で、

  • 4 サイクル命令の 1 サイクル目

を実行中であったことを示しています。
期待値は約 13 % で、大きくは違いません。
ICR1 の値が 0xFFFF 以下 (グラフでは -1 以下) の領域では、前に述べたように、オーバーフローが発生していないのは「物理的」に保証されているので、TOV1 の値を調べる必要はありません。
TOV1 が観測される領域が -26 程度まで延びているのは、単に、CAPT 割り込みルーチン内で実際に TOV1 フラグをチェックするまでに要する時間、つまり、

  • スリープ復帰
  • 割り込みシーケンス
  • SREG, R0, R1 などをスタックに退避する、割り込みルーチンのプロローグ部分

などを実行している間に、OVF 割り込み要求が「後追い」で発生したのを見ているにすぎません。
CAPT と OVF のタイミング関係の話は、これで一応終了とします。