さまざまな割り込み【STM32の割り込み詳細】
ハードウェアを制御する組み込みプログラミングでは割り込み処理を避けては通れないでしょう。STM32マイコンの割り込み処理はARMコアCortex-M3のルールに基づいているため、ルールの仕組みを理解して割り込み処理設定や処理内容を記述する必要があります。この章ではARMコアCortex-M3特有の割り込みについてプログラム例を使用して解説しています。
割り込みとは
システムクロックタイマの章でも割り込みが登場しましたが、この章ではSTM32マイコンで扱う割り込みを解説します。割り込みとは実行中のプログラムを一時中断して、特定の処理を行うことです。
割り込み機能はタイマをはじめ、ADコンバータ、UARTシリアル通信などのペリフェラルにもそれぞれ備わっていますので組み合わせて使用すれば、CPUの負担を減らし、効率のよいシステムを構築できます。
ここでは前章のながれでタイマに備わった割り込みの機能を使って解説していきます。
割り込み処理の設定
前章ではタイマを出力比較モードで入力クロックのカウント数をキャプチャコンペアレジスタ(CCR)で設定した値と比較してGPIO出力ポートを操作していました。ここではキャプチャコンペア割り込みを使った場合をみていきましょう。
割り込みを利用するためにはどのペリフェラルを使う場合も共通することですが、各ペリフェラルの設定の他に
① 割り込み初期化
② 割り込みの有効化
③ 処理内容を記述した割り込みハンドラ関数の作成
が必要です。
それではプログラムでの設定を解説していきましょう。
① 割り込みの初期化:
ペリフェラル(周辺機能)の割り込みはARMコアマイコンではNVIC(Nested Vectord Interrupt Contoller)が管理しています。NVICとはネスト型ベクタ割り込みコントローラのことで、ネスト型(多重)とは複数の割り込みが発生したときに優先順位の高いものから順次処理するタイプのことです。
ベクタは割り込みの要因を示す固有の番号のことで割り込みが発生した場合に割り込み要因に応じた処理項目を定めたベクタテーブルと呼ばれる対応表にまとめています。割り込みベクタによって、どのような割り込みが発生したかが分かるようになっています。
ARM Cortex-M3コアマイコンではNVICがペリフェラルの割り込みをすべて管理しているので割り込みの初期化はNVICの設定により行われます。NVICは各ペリフェラルで発生した割り込みをどの優先順位でどう処理するかの設定をする部分です。
NVICの設定はNVIC_Init関数で行います。指定するパラメータは構造体変数のメンバで4種類あります。
NVIC_IRQChannelに設定するのは使用する割り込み種別でNVIC_IRQChannel一覧表から選択します。これらはライブラリstm32f10x.h内に定義されたマクロです。例えば、TIM5のCH4キャプチャコンペア割り込みではTIM5_IRQn(TIM5グローバル割り込み)が相当しています。
複数の割り込みが発生しているときにどの割り込みから処理するかを決めるのがグループ優先度です。NVIC_IRQChannelPreemptionPriorityに 0から15まで指定でき、数値が小さくなるほど優先度は高くなります。0が最優先です。
例えば、グループ優先度1の割り込み処理Aを実行中にグループ優先度0の割り込み処理Bが発生すると処理Aを中断して処理Bに移ります。また、グループ優先度0の割り込み処理Bの実行中にグループ優先度1の割り込み処理Aが発生しても、割り込み処理Bは継続します。割り込み処理Aを実行中に同じグループ優先度の割り込み処理Cが発生すると、割り込み処理Aは継続し、終了後処理Cに移ります。
同じグループ優先度を持つ複数の割り込み処理が同時に発生していて実行待ちになっている場合に次にどの割り込み処理から処理するかを決めるのがサブ優先度です。NVIC_IRQChannelSubPriorityに0から15まで指定でき、数値が小さくなるほど優先度は高くなります。0が最優先です。
優先度は4ビット分の16通り(0 – 15)の割り当てができるのですが、STM32ではグループ優先度、サブ優先度の割り当ては組み合わせに指定があり、それぞれが0から15までの16通りで割り当てできるのではなく、グループ優先度とサブ優先度合わせて4ビット分つまり、16通りの割り当てをするようになっています。
この優先度の割り当て方法をNVIC_IRQChannelPreemptionPriority関数で指定します。
この関数で指定を行わない場合はNVIC_PriorityGroup_4を指定した時と同様、グループ優先度のみが有効となります。
NVIC_IRQChannelPreemptionPriority()関数の実行例:
NVIC_IRQChannelPreemptionPriority(NVIC_PriorityGroup_4);
割り込みの優先度設定は複数の割り込みが同時に発生するような場合に使われるものですが、比較的単純な割り込みの場合は、グループ優先度のみ(NVIC_PriorityGroup_4)の設定でよいと思います。
② 割り込みの有効化:
割り込みの初期化でNVICを設定した後は、各ペリフェラルで割り込みを発生させてNVICに対してどんな割り込みが発生したかを通知するための設定を行います。タイマ割り込みの例ではTIM_ITConfig関数を使用します。
TIM_ITConfig()関数の実行例:
TIM_ITConfig (TIM5, TIM_IT_CC4, ENABLE);
第1引数は設定対象タイマを指定します。
■ 設定対象タイマ : TIM1 – TIM8
第2引数は有効にする割り込みを下記いずれかのマクロで指定します。
第3引数は有効か無効を指定します。
■ ENABLE : 有効
■ DISABLE : 無効
以上でペリフェラルの割り込みは有効になりました。
③ 割り込みハンドラ関数を作成:
割り込みが発生すると対象の割り込みハンドラ関数が呼び出され、関数内の処理が実行されます。
ハンドラ関数名は決まった名称が定められておりこれもNVIC_IRQChannel一覧表に記載していますので参照してください。ここではTIM5_IRQHandler()関数を使用します。
TIM5の場合、割り込みが発生するたびに割込みハンドラ関数TIM5_IRQHandler()関数が呼び出されるのですが、割り込み内容は数種類あり、どの割り込みが発生したかはわかりません。
対象の割り込みが発生しているかどうかを割り込みステータスフラグをチェックしてから必要に応じて割り込み処理の実行に移ります。割り込み発生原因の確認にはTIM_GetITStatus関数を使用します。
割り込みステータスの実行例:
if(TIM_GetITStatus(TIM5, TIM_IT_CC4) != RESET){
TIM_ClearITPendingBit(TIM5, TIM_IT_CC4);
[割り込み時の処理]
}
関数の第1、第2引数はTIM_ITConfit関数と同じです。この関数はステータスフラグを示す戻り値があって、
■ SET : 割り込みが発生している
■ RESET : 割り込みは発生していない
上記例ではステータスフラグがRESETでないならばTIM5のCH4キャプチャコンペア割り込みが発生しているため、割り込み時の処理を実行する流れです。
割り込みステータスフラグは割り込み処理が完了したあとに自動的にはクリア(リセット)されません。そこで、次の割り込みに対応するためには強制的にクリアしておく必要があります。割り込みステータスフラグをクリアにするにはTIM_ClearITPendingBit関数を使用します。
割り込みステータスフラグクリア関数の実行例 : TIM_ClearITPendingBit(TIM5, TIM_IT_CC4);
この関数の第1、第2引数もTIM_ITConfit関数と同じです。
割り込みステータスフラグクリア関数は割り込みハンドラが呼び出され、割り込み処理を実行する直前に実行してフラグをクリアしておくのが好ましいです。また、割り込みを有効化する直前にもリセットしておきましょう。
ここで割り込みを使用する準備が完了しましたのであとは割り込み時の処理を記述するだけです。通常、割り込みでは複雑な処理は実行させず、簡単な処理だけにしておくほうがよいです。割り込みである以上、ほかの処理を中断しているからです。
例えば、シリアル通信の受信があったときに割り込みを発生させる場合には割り込み処理で受信処理すべてを実行するのではなく、受信通知フラグ(受信時に1、それ以外では0)をたてるだけで、フラグの状態に応じて処理自体はメインプログラムで行うようにすれば割り込み処理の負担はなくなります。
外部入力による割り込み
これまでは汎用タイマTIM5で割り込み動作の例を紹介してきました。次に外部入力による割り込みを紹介します。
PD10の入力を割り込みとするアプリでは入力ポートPD10に信号がはいると通常処理が中断して割り込みハンドラEXTxxxx_IRQHandler()関数に処理が移ります。
割り込み発生時に実行したい処理はこの関数内に記述します。割り込みを使用したプログラムの流れは各周辺機能-さまざまな割り込みの項で説明したとおりです。各ペリフェラルの割り込む機能を使用するかわりに外部入力信号を割り込みのトリガにするところが異なるだけです。
下例では省略していますが、割り込み信号ピンに使用するGPIOにもクロックを供給し、ピンを入力に設定しておきます。(PD_10)
① AFIOクロックの供給:
EXTIを使用する場合はAFIOクロックを供給しておく必要があります。
② 外部入力初期化:
まず、GPIO_EXTILineConfig関数を実行して指定のGPIOポートをEXTIラインに接続します。関数の第1引数にはポートx(AからG)まで(GPIO_PortSourceGPIOx)を指定、第2引数にはピン番号y(0から15)まで(GPIO_PinSourcey)を指定します。
GPIO_EXTILineConfig関数実行例(PD10):
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource10);
初期化はEXT_Init関数を実行します。関数の引数は構造体メンバになっていて以下に示す設定パラメータを含んでいます。
外部入力割り込みEXTI初期化関数実行例: EXTI_Init(&EXTI_InitStructure);
EXTI_Lineメンバでは割り込みに使用する入力ポートを指定します。EXTIラインは20本あり、ピンx(0-15)までの16本はピン番号に相当したEXTI_Linexを指定します。他の4本EXTIライン16-19はイベントラインによるウェイクアップを行う場合に使用します。
EXTI_ModeメンバではEXTIを割り込みモードで使用するかイベントモードで使用するかを指定します。
EXTI_Triggerメンバでは割り込み・イベントを発生させるラインエッジを指定します。
EXTI_LineCmdメンバではEXTIラインの有効・無効を指定します。
③ 割り込みの初期化:
外部入力割り込みの場合は、構造体変数のメンバのNVIC_IRQChannel に ピン番号に応じたIRQチャネルを指定します。ピン10の場合はEXTI15_10_IRQnです。その他の設定は他の割り込みと同様です。
④ 割り込みハンドラに処理内容を記述:
割り込みにより呼び出された関数(割り込みハンドラ)に処理したい内容を記述します。
呼び出されるハンドラは割り込みラインにより特定のハンドラが呼び出されます。割り込みNVIC_IRQChannel一覧表に記載したとおりです。
ピン0 から 4まではそれぞれに対する割り込みハンドラEXTI0_IRQHandler()からEXTI4_IRQHandler()が独立して呼び出されるのですが、ピン5から9まではEXTI9_5_IRQHandler()が、ピン10から15まではEXTI15_10_IRQHander()が呼び出されます。したがって、ピン5から15までの場合は呼び出された割り込みハンドラ内でどのピンからの割り込みなのかの識別のために、EXTI_GetITStatus(EXTI_Linex)ピン番号x(5-15)で確認する必要があります。
割り込みごとにフラグをクリアする必要がありますので、EXTI_ClearITPendingBit関数を使用します。
割り込みステータスフラグクリア関数の実行例 : EXTI_ClearITPendingBit(EXTI_Line10);
この関数の引数もEXTI_GetITStatus関数と同じです。
割り込みにはほかにADコンバータ、UARTシリアル通信などの割り込みがあります。ペリフェラルライブラリでの関数名や、引数名は異なりますが使い方の流れはタイマのときとほぼ同じです。実際の使用方法につきましては改めて、アプリケーションと実際の章でプログラムを紹介したいとおもいます。