STM32で各種割り込み機能を使うには、__weak属性関数を上書きしてその中に必要な機能を実装していきます。
実際の作業手順を、インターバルタイマ割り込みを例にして具体的に説明します。
今回の例ではSTM32F103ZETxを使っていますが、STM32の他シリーズでも同様の作業手順になります。
CubeMXでの割り込み設定
この項では、CubeMX上でインターバルタイマ(1ms)割り込みの設定を行い、コード生成までを行います。クロック周波数の設定と、タイマーペリフェラル機能の設定、割り込みの設定となります。
クロック周波数の設定
CubeMXを立ち上げたら、Clock & Configuration タブにてクロックの設定を行います。
設定内容は、1msのインターバルタイマ機能を使うだけであればどのような設定でも構いません。他に使いたい機能優先で決めてしまって良いです。
設定が終わったら、タイマーに供給されるクロックの周波数を確認しておきます。
タイマーに供給されるクロックには2種類あり、タイマーモジュールによってどちらのクロックが供給されるのかは決まっています。下の赤枠のところを参照してください。
この例では、どのタイマーにも72MHzのクロックが供給されるように設定してあります。
同じSTM32マイコンであっても、別型番になればクロックの構成がかなり違ってきます。対応するデータシートを確認して設定してください。
タイマーペリフェラル機能の設定
タイマーペリフェラル機能の設定には、Pinout & Configuration → Categories → Timers → TIM6 を選択します。
Activatedにチェックを入れて、Parameter Settingsタブで下のように設定します。
設定の詳細は下記になります。カウンタが0スタートなので、欲しいカウント数-1が設定値になることに注意してください。
①Prescalerを”71” と設定
タイマークロックが72MHzなので、Prescaler = 1MHz に設定されます。
②CounterPeriodを”999”と設定
Prescaler(1MHz)を 1000カウント = 1ms と設定できました。
③auto-reload preload をEnable
カウンターが自動で繰り返し動作するようになり、インターバルタイマとして機能します。
数値入力欄では簡単な計算を自動でしてくれます。
たとえば、72-1 と入力することで、71 と設定できます。
割り込みの設定
次に、NVIC SettingタブにてTIM6 global interrupt のEnableにチェックを入れます。これで割り込みが有効になります。
これでCubeMXでの設定は完了です。セーブしてコード生成をしましょう。
Categories → System Core → NVIC からも割り込み有効化設定は出来ます。
割り込みの一覧表を見ながら優先順位も決められるので、多重割込みする場合などはこちらが使いやすいかも知れません。
タイマーの動作開始を行う
コード生成をしただけでは、タイマーは初期化されただけで動作していません。
下記のHAL関数で動作開始を行います。
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
特別な理由が無ければ、初期化を行った直後に動作開始すれば良いです。
その場合は main.c 内の
/* USER CODE BEGIN 2 */ と /* USER CODE END 2 */ の間
に記述します。下のコードのようになります。
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USB_DEVICE_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6); //追加した行
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
関数引数の &htim6 がタイマーハンドラになります。別のタイマーを使った場合は適宜変更してください。ハンドラ自体は main.c で定義されています。
自動生成されたコードに追記する場合は、必ず下記のようなコメントの間に追記するようにしてください。
/* USER CODE BEGIN xxx */
/* USER CODE END xxx */
このコメント間に追記されたコードは、コード生成をし直した場合にも保持されます。
割り込みの処理を記述
割り込みコールバック関数
最後に割り込み発生時の処理を記述していきます。
自動生成されたコードを確認していくと、割り込み発生時に下記のコールバック関数が呼び出されていることがわかります。
__weak void HAL_TIM_PeriodElapsedCallback(
TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
/* NOTE : This function should not be modified,
when the callback is needed,
the HAL_TIM_PeriodElapsedCallback could be
implemented in the user file
*/
}
一見、この関数の中に処理を記述していけば良さそうですが、コードを追記するためのコメントが無い代わりに変更不可との注意書きがコメントされています。
関数の先頭にも見慣れない”__weak”という記載があります。
__weak属性関数とは?
この”__weak”とは何なのでしょうか?
”__weak” キーワードは、”stm32f1xx_hal_def.h”ファイルの中で下記のように定義されています。
#define __weak __attribute__((weak))
要するに、__weakというのは”弱い”関数定義という意味です。
弱いとはどういう意味かというと、別のところで”__weak”を付けずに同名の関数定義をすると、そちらの関数が優先してビルドされるという事です。
このような特性は、デフォルトの処理を定義しておくのに便利です。もしアプリケーションプログラマがその関数内で何か特別なことをしたいと思えば、別のファイルで同名の関数を記述してしまえば良いのです。
STM32のHALコードにおいては、割り込みのコールバック関数には__weak属性が付けられています。
つまり、コールバックを記述する際は、__weak関数と同名の関数を独自に記述すればよいという事になります。この方法は、タイマー割り込みに限らず他の割り込みでも同様です。
上書きする方法
それでは、別ファイルに HAL_TIM_PeriodElapsedCallback 関数を定義してみます。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim6){
/*User Callback Code*/
}
}
ここでのポイントは、引数のハンドラが何か確認しているところです。インターバルタイマに設定したhtim6以外のタイマーで割り込みが発生したときもこの関数がコールされますので、割り込みを発生させたハンドラは何なのかを確認しなければなりません。
最後に、/*User Callback Code*/ のことろに必要な機能を記述して完成です。
まとめ
STM32で各種割り込み機能を使うには、__weak属性のついた割り込みコールバック関数を上書きすれば良いです。
このコールバック関数は、同種のペリフェラル機能で共有されていますので、引数のハンドラをチェックして処理を分岐させてやる必要があります。
他のマイコンの自動生成コードと比べるとなんだか回りくどい事をしているような印象で、少々分かり難く感じたかもしれません。ただし、STM32では他の割り込みも同様の構成なので一度やり方を覚えればOKです。
その他のHowto記事
STM32のソフト開発に関するHowtoは他にも記事があります。こちらも参考にしてください。
コメント