1 SSTM32CubeIDEの文法
大げさに文法と記載しましたが、ここではSTM32CubeIDEでレジスターにアクセスする際のプログラム表現方法は、主に以下の2つの方法で、異なります。
1.1 HALを使用する場合
STM32CubeIDEでプロジェクトを作成すると、自動的に生成される「Hardware Abstraction Layer (HAL)」または「Low-Layer (LL)」ライブラリを使ってレジスターにアクセスするのが一般的です。
この場合、直接レジスター名を記述する必要はありません。
コード例(HALライブラリ):
// GPIOAのピン5をHighにする(点灯)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
// GPIOAのピン5をLowにする(消灯)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
■その他
URL
https://happytech.jp/wordpress/2019/04/06/stm32cubemx-low-layer-api-usage-1/
で STM32CubeMXのLow-Layer API利用法 (1)が記載されていれる
https://happytech.jp/wordpress/tag/stm32cubemx/page/3/
で STM32CubeMXのLow-Layer API利用法 (3)が記載されていれる
この場合、直接レジスター名を記述する必要はありません。
コード例(HALライブラリ):
// GPIOAのピン5をHighにする(点灯)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
// GPIOAのピン5をLowにする(消灯)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
■その他
URL
https://happytech.jp/wordpress/2019/04/06/stm32cubemx-low-layer-api-usage-1/
で STM32CubeMXのLow-Layer API利用法 (1)が記載されていれる
https://happytech.jp/wordpress/tag/stm32cubemx/page/3/
で STM32CubeMXのLow-Layer API利用法 (3)が記載されていれる
1.2 直接レジスターを操作する場合
より低レベルな制御を行いたい場合は、C言語の構造体とポインターを利用して、直接レジスターにアクセスします。
この場合、レジスターの正確な名前とビット位置を知っている必要があります。
コード例(直接レジスター操作):
// GPIOAの出力データレジスター(ODR)のビット5をセットする
GPIOA->ODR |= (1 << 5);
// GPIOAの出力データレジスター(ODR)のビット5をクリアする
GPIOA->ODR &= ~(1 << 5);
この方法では、GPIOAはGPIO Aポートのベースアドレスを指すポインターで、ODRはその構造体のメンバーとして定義されています。
レジスターを直接操作する際は、該当ペリフェラルのクロックを有効にする必要があります。
直接レジスターを操作するプログラムは、HAL/LLライブラリを使用するプログラムよりも移植性が低くなる場合があります。
プロジェクトの用途やプログラミングの習熟度に応じて、どちらの方法を選択するか検討してください。
この場合、レジスターの正確な名前とビット位置を知っている必要があります。
コード例(直接レジスター操作):
// GPIOAの出力データレジスター(ODR)のビット5をセットする
GPIOA->ODR |= (1 << 5);
// GPIOAの出力データレジスター(ODR)のビット5をクリアする
GPIOA->ODR &= ~(1 << 5);
この方法では、GPIOAはGPIO Aポートのベースアドレスを指すポインターで、ODRはその構造体のメンバーとして定義されています。
レジスターを直接操作する際は、該当ペリフェラルのクロックを有効にする必要があります。
直接レジスターを操作するプログラムは、HAL/LLライブラリを使用するプログラムよりも移植性が低くなる場合があります。
プロジェクトの用途やプログラミングの習熟度に応じて、どちらの方法を選択するか検討してください。
1.3 処理速度の比較
IDEで記載したプログラム
ボード:NUCLEO-F446RE
出力 Port : PA1
OUTPUT level : Low
GPIO mode : Output Push Pull
GPIO Pull-up/down : Pull-up
Maximum output speed : Very Hight
Clock Configuration : HCLK=180MHz
プログラムは下記のmain.Cの青字部分を追加した。

■注意:表の値について
1.時間(ns)は実際測定した時間ではなく、処理クロックが整数となるように実測値に近い値になっている。
2.1クロック時間の計算方法は HCLK = 180MHz = 1周期(サイクル) = 1クロック から
1×109/(180×106)≒ 5.5(ns)としている。
■判ったこと
1.HALより直接レジスターを操作したほうが早く処理が出来る。
2.⑨〜⑭はシフトしているので⑮〜⑳と比べ4サイクルも違っている
■不明な点は
1.③の部分がなぜ早くなったか:通常198nsが143nsで55ns(10サイクル)早くなっている
2.⑨の部分がなぜ遅くなったか:通常55nsが99nsで44ns(8サイクル)遅くなっている
3.最速で動作させている⑮〜⑳で33ns=6サイクルも必要なのか?
4.⑩、⑫、⑭は⑨、⑪、⑬に反転が有るが処理速度に変化が無いのはなぜか?
今後も調べて行きたい。
ボード:NUCLEO-F446RE
出力 Port : PA1
OUTPUT level : Low
GPIO mode : Output Push Pull
GPIO Pull-up/down : Pull-up
Maximum output speed : Very Hight
Clock Configuration : HCLK=180MHz
プログラムは下記のmain.Cの青字部分を追加した。
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1)
{
GPIOA->BSRR =0b00000000000000000000000000000010; //① PA1 ON
GPIOA->BSRR =0b00000000000000100000000000000000; //② PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //③ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //④ PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //⑤ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //⑥ PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //⑦ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //⑧ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑨ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑩ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑪ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑫ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑬ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑭ PA1 OFF
GPIOA->ODR = 2; //⑮ PA1 ON
GPIOA->ODR = 0; //⑯ PA1 OFF
GPIOA->ODR = 0b0000000000000010; //⑰ PA1 ON
GPIOA->ODR = 0b0000000000000000; //⑱ PA1 OFF
GPIOA->BSRR =0b00000000000000000000000000000010; //⑲ PA1 ON
GPIOA->BSRR =0b00000000000000100000000000000000; //⑳ PA1 OFF
HAL_Delay(1);//1ms待つ
}
}
PA1の電圧波形{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1)
{
GPIOA->BSRR =0b00000000000000000000000000000010; //① PA1 ON
GPIOA->BSRR =0b00000000000000100000000000000000; //② PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //③ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //④ PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //⑤ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //⑥ PA1 OFF
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //⑦ PA1 ON
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); //⑧ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑨ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑩ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑪ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑫ PA1 OFF
GPIOA->ODR |= (1 << 1); //⑬ PA1 ON
GPIOA->ODR &= ~(1 << 1); //⑭ PA1 OFF
GPIOA->ODR = 2; //⑮ PA1 ON
GPIOA->ODR = 0; //⑯ PA1 OFF
GPIOA->ODR = 0b0000000000000010; //⑰ PA1 ON
GPIOA->ODR = 0b0000000000000000; //⑱ PA1 OFF
GPIOA->BSRR =0b00000000000000000000000000000010; //⑲ PA1 ON
GPIOA->BSRR =0b00000000000000100000000000000000; //⑳ PA1 OFF
HAL_Delay(1);//1ms待つ
}
}

プログラムNo | ② | ③ | ④ | ⑤ | ⑥ | ⑦ | ⑧ | ⑨ | ⑩ | ⑪ | ⑫ | ⑬ | ⑭ | ⑮ | ⑯ | ⑰ | ⑱ | ⑲ | ⑳ |
時間(ns) | 33 | 143 | 231 | 198 | 231 | 198 | 231 | 99 | 55 | 55 | 55 | 55 | 55 | 33 | 33 | 33 | 33 | 33 | 33 |
処理クロック | 6 | 26 | 42 | 36 | 42 | 36 | 42 | 18 | 10 | 10 | 10 | 10 | 10 | 6 | 6 | 6 | 6 | 6 | 6 |
1.時間(ns)は実際測定した時間ではなく、処理クロックが整数となるように実測値に近い値になっている。
2.1クロック時間の計算方法は HCLK = 180MHz = 1周期(サイクル) = 1クロック から
1×109/(180×106)≒ 5.5(ns)としている。
■判ったこと
1.HALより直接レジスターを操作したほうが早く処理が出来る。
2.⑨〜⑭はシフトしているので⑮〜⑳と比べ4サイクルも違っている
■不明な点は
1.③の部分がなぜ早くなったか:通常198nsが143nsで55ns(10サイクル)早くなっている
2.⑨の部分がなぜ遅くなったか:通常55nsが99nsで44ns(8サイクル)遅くなっている
3.最速で動作させている⑮〜⑳で33ns=6サイクルも必要なのか?
4.⑩、⑫、⑭は⑨、⑪、⑬に反転が有るが処理速度に変化が無いのはなぜか?
今後も調べて行きたい。