この記事は ARM Cortex-M シリーズ全て(M0 はオプションなのでついていない可能性あり)に CPU コアの一部として 付属している簡易的な設定で動いてしまうタイマです。本記事では STM32 を用いてこの設定をまとめておきます。
カウント仕様
概ね以下のページで仕様は網羅してあるのですが、ここでもう一度まとめます。
カウンタのビット数
このタイマーについているカウンタは 24ビット です。ですので値がこの範囲に収まるようにせねばなりません。例えば油断して
SysTick->LOAD = SystemCoreClock;
なんてやると、例えばシステムクロックが 64MHz の時は 24bit に収まらずして変な更新周期となってしまいます。
カウント方向
一般的な(?)タイマーと違ってこれは ダウンカウンタ です。Cortex-M シリーズユーザーガイドに依ると、SysTick->LOAD + 1
の周期で割り込みフラグが入るとのことです。
クロック選択
SysTick タイマのクロックソースは2種類あって、1つが CPU クロック、もう1つが 外部クロック です。この外部クロックは外部水晶とは限らず、各マイコンメーカーによって決められたクロックを用います。例えば STM32 ですと HCLK(CPU クロック) を 8 分周したものとなっています。レジスタでは SysTick_CTRL_CLKSOURCE_Msk
を OR してあげれば前者になります。
カウント値
基本的にリードオンリーです。書き込みを行うと、その値にかかわらず SysTick->VAL
が0になります。この時、割り込みフラグは立ちません。なので
SysTick->VAL = 114514;
としてもカウントリセットになります。
割り込み
Cortex-M シリーズでは SysTick タイマの割り込み優先度は最高にしてあります。また、NVIC コントローラから割り込み有効/無効の設定は出来ません。デフォルトで有効になっています。というか、割り込みの制御は NVIC 側ではなく、SysTick 側で行います。具体的には SysTick->CTRL
の TICKINT
ビットで設定できます。
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // enable interrupt
逆に NVIC_EnableIRQ(SysTick_IRQn)
をやると領域外アクセスで落ちます。この関数の中身は以下のようになっているようです。
/**
\brief Enable External Interrupt
\details Enables a device-specific interrupt in the NVIC interrupt controller.
\param [in] IRQn External interrupt number. Value cannot be negative.
*/
__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
コメントにあるように、IRQn
にはマイナスの値は取れません。これが落ちる原因です。実際 SysTick_IRQn
は enum
で
SysTick_IRQn = -1
と定義されています。ですので何度も言いますが SysTick の割り込み有効化に NVIC_EnableIRQ
は使ってはいけません。
ということで実装
割り込みフラグは使うが実際に割り込みを入れないというシチュエーションは 簡易 1msec タイマー を作る回で載せているので割愛します。
本記事では割り込みを使う場合のみ実装例を示します。
設定
以下は 1sec ごとに割り込みを起こさせる例です。なお CPU クロックは 64MHz、使用マイコンは STM32 とします。
まず最初にカウンタのリセット、リロード値セットをします。リロード値セットは迂闊に 64e6 - 1
とするとこれは24ビットに収まりませんのでだめです。よって STM32 の仕様どおり、8分周したクロックを使用することにします。また、ペリフェラルのクロック供給設定がいらないのがミソにもなります。
SysTick->VAL = 810; // reset to zero
SysTick->LOAD = 8e6 - 1;
続いて割り込みの有効化です。TICKINT
ビットを立てれば良いのでしたね。ついでにカウントスタートもしてしまいましょう。
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // enable interruput on the SysTick side
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // count start
続いて割り込み関数です。TIMx
のようにステータスフラグをクリアする必要もなく、とりあえずやりたい処理を書けば良いのです。例えば PF0
の L チカ。もちろんピン設定等は事前に行っておいてください。
void SysTick_Handler(void) {
GPIOF->ODR ^= 1;
}
たったこれだけで設定完了です。
また、この情報は以下の YouTube 動画を参考にしています。
このレクチャーシリーズは結構分かりやすいので皆さん見てみてくださいね。