STM32 IDEで LL GPIO の初期化コードを出力する

  • 2020.12.25
  • LL
STM32 IDEで LL GPIO の初期化コードを出力する

皆さま こんにちは。

今回は IDE で HALとLL それぞれについて GPIO の初期化コードを出力してみます。

投稿時の開発環境を記しておきます。

( CubeIDEとCubeMXの開発環境を以下の通りにバージョンアップしました )

PC:Windows10 OS
IDE: STM32CubeIDE Version1.5.0
Configurator: STM32CubeMX Version6.1.0
Board: STM32Nucleo-F401RE

まずHALプロジェクトから

これまで意識して来ませんでしたがIDEにおいてデフォルトの初期化コードはHALが使われることになっています。

復習も兼ねて上記ボードのプロジェクトを生成してみます。

IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。

Project 名に F401GpioHAL と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。

ビルドする

Project – Build All を選択します。

Console にはエラーのないことが表示されます。

07:12:16 Build Finished. 0 errors, 0 warnings. (took 3s.145ms)

ボードをつないでデバッグモードにする

USBケーブルでPCとボードをつなぎます。

Project Explorer で F401GpioHAL をクリックして選択し、Run – Debug を選択します。

This kind of launch is configured to open tihe Debug porspective when it suspends.
this Debug perspective supports application debugging by providing views for displaying the debug stack, variables and breakpoints.
Do you want to switch to this perspective now?

(停止中にデバッグ画面を表示しますか?)

と聞いてくるので、Remenber my decision のチェックボックスにチェックを入れて、switchボタンを押します。

ROMとRAMの使用状況を確認する(HAL)

Window – Show View – Build Analyzer を選択します。

Memory Regionsタブが選択されていてRAM, ROMの使用量が確認できます。

RAM Used 1.61KB
ROM Used 7.9KB

となっています。
何%使ったのかわかるので便利ですね。

Build Analyzerの関連記事 も参考にどうぞ。

main.cを眺めてみるとHALを使ったGPIOの初期化コードは以下のようになっています。

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

GPIOの初期化コード量(HALの場合)

Build Analyzer で Memory Detailes を選択し FLASH – .text とツリーを降りていくと MX_GPIO_Initが見つかりました。

Size が 224B (Byte) となっています。

関数単位でどれだけROMを消費しているのかわかるので便利ですね。

LLでGPIOを初期化するプロジェクトをつくってみる

それでは LL の方にいってみましょう。

先ほどのプロジェクトを実行中であれば、 Run – Terminate (または四角い赤のボタン)を選択して動作を停止しておきます。

プロジェクトはそのままで、もうひとつLL用のプロジェクトをつくります。

IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。

Project 名に F401GpioLL と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。

GPIOの初期化にLLを使う設定を行う

Project Managerタブを選択し Advanced Settings の GPIO を HAL (初期値)から LL に変更します。(コンボボックスで選択する)

Project – Build All を選択します。

Do you want Generate Code ? と聞いてくるのでRemenber my decision のチェックボックスにチェックを入れて OKを押します。

This action can be associated with C/C++ perspective.Do you want to open this perspective now?

と聞いてくるのでRemenber my decision のチェックボックスにチェックを入れて Yesを押します。

Project Explorer で F401GpioLL を選択し、Run – Debug を選択します。

Edit Configuration のウィンドウが出たら OKを押します。

ROMとRAMの使用状況を確認する(LL)

画面下部のペインで先ほど表示した Build Analyzer を選択します。

Memory Regionsタブを選択するとRAM, ROMの使用量が確認できます。

RAM Used 1.61KB
ROM Used 9.43KB

なぬぅ・・・、HALの時よりもコード領域がたくさん使われています。

main.cを眺めてみるとLLを使ったGPIOの初期化コードは以下のようになっています。

static void MX_GPIO_Init(void)
{
  LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);

  /**/
  LL_GPIO_ResetOutputPin(LD2_GPIO_Port, LD2_Pin);

  /**/
  LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTC, LL_SYSCFG_EXTI_LINE13);

  /**/
  EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_13;
  EXTI_InitStruct.LineCommand = ENABLE;
  EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
  EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_FALLING;
  LL_EXTI_Init(&EXTI_InitStruct);

  /**/
  LL_GPIO_SetPinPull(B1_GPIO_Port, B1_Pin, LL_GPIO_PULL_NO);

  /**/
  LL_GPIO_SetPinMode(B1_GPIO_Port, B1_Pin, LL_GPIO_MODE_INPUT);

  /**/
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

パッと見、HALの場合よりコード量が多そうですね。。

初期化よりも処理する部分のコード使用量に差が出てくるのかも知れません。

コンフィグレーターにより追加されるファイル

Project EXplorer で

Drivers\STM32F4xx_HAL_Driver\Src
Drivers\STM32F4xx_HAL_Driver\Inc

を覗いてみると、デフォルトの場合には HAL のドライバーだけが存在しますが、LLを選択した場合には LL のドライバーが入ってきます。

この中の関数を眺めてみると、お勉強になるかも知れません。

GPIOの出力の場合だと、

・0を書く
・1を書く
・出力を反転する

くらいの使い方なので比べてみると良いですね。
ここでは出力を反転する関数についてのみ比べてみることにします。

HAL では (stm32f4xx_hal_gpio.c)

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if ((GPIOx->ODR & GPIO_Pin) == GPIO_Pin)
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << GPIO_NUMBER;
  }
  else
  {
    GPIOx->BSRR = GPIO_Pin;
  }
}

LL だと (stm32f4xx_ll_gpio.h)

__STATIC_INLINE void LL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
  WRITE_REG(GPIOx->ODR, READ_REG(GPIOx->ODR) ^ PinMask);
}

注意点というか、LLの方の特徴はインライン関数になっていることでしょう。
(インライン関数はヘッダファイルの方に書かれています)

関数の恰好をしていますが、LLの方はインラインになっていることが多いようです。
インラインにするということで中身は非常にコンパクトに書かれていますね。

このコードを見る限り、速さで言えば断然 LL に軍配が上がります。

仮にHALの方に if()が なくてでもですね。

スピード優先であれば、関数呼び出しのオーバーヘッドも気にかけて LL を使うべきでしょうか。

※インライン関数はサブルーチンコールではなく、その場にコードを埋め込むのでスタック操作の処理時間を省くことができます。

(注)コンパイラの最適化オプションによって、埋め込むのかサブルーチンコールになるのかがかわってきます。
Optimize for speed を選択すると、埋め込まれることが確認できました。

ここまでGPIOを例に見てきましたが

・コード量を減らさねばならない
・実行速度を上げなければならない

ような状況では、LLを試してみる価値は大いにあるのではないかと思います。

ただしレジスタのマニュアルをよく読まないといけない とか
関数の使い方がわかりにくい(資料が少ない) とか
取り扱う上での難点も多いようです。

それでは今回はこのへんで。

お疲れさまでした。

LLカテゴリの最新記事