エビデイエビナイ電子工作

やったことの記録

STM32のレジスタを叩く。 #1 ~GPIO編~

f:id:Rappy:20191114111502j:plain

今回はGPIO編です。早速Lチカからやってみましょう。

エディタでSrcディレクトリにあるmain.cを開くと、CubeMXが生成した初期化コードが記述されています。

この初期化コードには、CubeMXで設定したピンの機能やクロックの設定等が反映されています。CubeMXを利用することで、これらの設定をGUIで行うことができ、開発をスピーディーに進められます。


...が、このシリーズではクロックの設定も含めてレジスタから行うのでmain.cの中身を全て消します。(涙)

代わりにstm32f4xx.hをインクルードしておきましょう。

#include "stm32f4xx.h"

また、今回Lチカに使うLEDは、NUCLEO-F446REにはじめから実装されているもの(LD2)とします。

GPIOの設定

RM0390(リファレンスマニュアル)から設定が必要なレジスタを探します。

GPIOx_MODER

f:id:Rappy:20191116150051p:plain
GPIOx_MODER
このレジスタではピンのモード(入出力方向など)を設定します。

Reset valuesを見ると、一部のピンを除いて"00 (Input)"で初期化されています。 LD2はPA5に接続されているので、GPIOAのMODER5を"01 (General purpose output mode)"に設定します。 具体的には次のように記述します。

GPIOA->MODER |= (1 << 10);

これでもよいのですが、1や10といった数字が何を表すのか分かりづらく、可読性が高いとは言い難いです。 stm32f446xx.hには、こういったレジスタの設定に便利な定義がたくさん用意されています。

f:id:Rappy:20191116150735p:plain
stm32f446xx.h
同じくstm32f4xx_hal_gpio.hにも便利な定義があるのでこれも使っちゃいましょう。
f:id:Rappy:20191116180159p:plain
stm32f4xx_hal_gpio.h
すると、

GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODER5_Pos);

このように記述することが出来ます。先ほどの例と比べて、この行で何が行われているのかが分かりやすくなったかと思います。

GPIOx_ODR

f:id:Rappy:20191116150053p:plain
GPIOx_ODR
GPIOx_ODRは各ポートが出力する値を設定するレジスタです。 LEDを点滅させるにはPA5の出力を一定時間ごとにトグルさせれば良いので、適当にforで待ちを作った後、GPIOAのODR5ビットを反転させます。

while(1){
    for(volatile uint32_t i=0;i<1000000;i++);
    GPIOA->ODR ^= GPIO_ODR_OD5;
}

これで完了です!


...と言いたいところですが、STM32では省電力のため、ペリフェラルへのクロック供給がデフォルトで無効になっています。GPIOを使うにはこれを有効にする必要があります。

f:id:Rappy:20191117221105p:plain
STM32F446xC/E のブロック図

データシートのブロック図を見ると、GPIOAはAHB1に接続されていることがわかります。

RCC_AHB1ENR

f:id:Rappy:20191117203325p:plain
RCC_AHB1ENR
AHB1に接続されているペリフェラルへのクロック供給を有効/無効化するためのレジスタです。 GPIOAを使うには、このレジスタGPIOAENビットを立てる必要があります。

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

今度こそ完了です。 以上のことを踏まえてコードを示します。

#include "stm32f4xx.h"

int main(void){
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    GPIOA->MODER &= ~GPIO_MODER_MODER5_Msk;
    GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODER5_Pos);

    while(1){
        for(volatile uint32_t i=0;i<1000000;i++);
        GPIOA->ODR ^= GPIO_ODR_OD5;
    }
    return 0;
}

前回同様 makeして書き込むと、LEDが点滅しました。

www.youtube.com


twitter.com