GPIOを使ってボタン入力をする

前回はマイコンのピンの電圧制御にはGPIOペリフェラルをいじれば良いということがわかりました。
ODRレジスタの対応するビットを操作することでONOFFしていましたが、今回は逆に入力をやってみましょう。

入力とは

STM32F4Discoverにはボタンが2つあり、うち1つは自由に使うことが出来ます。

IMG_06703

この青いボタンがそうです。このボタンがマイコンのポートAの0につながっています。

refmanu_btn
出典: ReferenceManual

回路図としてはこんな感じです。
ボタンが押されていない時はPA0は220kΩでGNDに繋がれ0Vになり、押されるとVDDに繋がり3.3Vになる(VDDは3.3Vです)感じです。
それがGPIOのポートAの0につながっています。
GPIOは1か0かでピンの電圧を3.3Vか0Vかにプログラムから切り替えられましたが、逆に高いか低いかを1か0かで入力する機能もあります。

MODERをもう一度見てみる

ここで、前回出てきたGPIOのMODERレジスタをもう一度見てみましょう。

Screen Shot 2016-01-05 at 22.16.17

前回はこれを元に青色LEDが繋がっている場所を10(出力モード)に切り替えました。これによると

  • 00 入力(reset state)
  • 01 出力
  • 10 AlternativeFunction
  • 11 アナログ

となっています。実は元々GPIOは全てのピンが最初から入力モードになっていて、ピンの状態をプログラムから読み取れる状態になっています。
では、読み取った値は出力を決めるのに使っていたODRに入るのかというと、そうではなく別の今度はIDRというレジスタに入ります。

Screen Shot 2016-01-23 at 18.00.00
出典:ReferenceManual

IDRのそれぞれの1bitがそのポートの状態を表しています。
今回ならGPIOAの0ですから、ボタンが押されていなければ(0V) GPIOAのIDRの0bit目は0。
押されていれば(3.3V)0bit目は1となります。

ボタンにあわせてLEDをつける

では、早速試してみましょう。
ボタンが押されていたらLEDをつけるようにします。

#include "stm32f4xx.h"

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

  GPIOA->MODER = 0;
  GPIOD->MODER = 0x40000000;
  while(1) {
    GPIOD->ODR = GPIOA->IDR << 15;
  };
}

まず、GPIOAのクロックを有効にして使えるようにします。
そしてMODERに0を代入して入力にします。元々起動した時から0になっていて入力なので、このプログラムは別に無くても問題無いです。
そして、while(1)の無限ループの中で前回1<<15;としてLEDのところに1をセットしていましたが、そこをGPIOA->IDRとしています。
こうすることで、GPIOAの0bit目がGPIODの15bit目に挿入されます。

IMG_0671

もちろんGPIODの16bit目にもGPIOAの1bit目が入るなど、他のところにも影響がありますが、今回は無視しています。

すごく簡単でしたね。
他にもボタンが押された時に何かのプログラムを動かしたいときは

if ( GPIOA->IDR & 1) {
//押された時のプログラム
}

ってすれば出来ますし、いろいろ使えます。
(ちなみにプログラムが苦手な人へ、何故while(1)という無限ループの中にボタンの入れたか謎に感じてると思います。
GPIOD->ODR = GPIOA->IDR << 15;
while(1);
これでいいじゃないかと。
ただ、これだとダメです。何故って、このODRにセットするプログラムは起動後一回呼ばれてその後はすぐwhileで無限に何もしないということをするわけですから、どんなにボタンを押してIDRを変えてもODRにはセットされないわけです。ボタンを押しながらマイコンの電源を入れた時だけ意味が出てきます。

GPIOの他のレジスタ

ここまでで

  • MODER ピンのモードを決める
  • ODR 出力値を決める
  • IDR 入力された値

という3つのレジスタがあることを知りました。
GPIOには他にもレジスタがあります。

OTYPER

GPIOの出力タイプをプッシュプルかオープンドレインかから選べます。
起動後はプッシュプルになっています。
プッシュプルというのはトランジスタ2つをつないでどっちかだけONにすることで3.3Vか0Vかを出力するというものです。普通ですね。

IMG_0672

オープンドレインというのはトランジスタ1つだけを使うものでGNDにつなぐかOFFかとなります。違う電圧っで出力したい時などに使います。例えばこの出力をつなぐ先が5Vで動作する他のマイコンだとすると、左のプッシュプルだとONにしても3.3Vにしかなりません。これだと相手の入力としては足りないです。しかしオープンドレインならこの出力を抵抗で5Vに繋げば 5Vか0Vかという出力ができます。

OSPEEDR

ピンの動作速度を4つから選べます。実はプログラムでON-OFF-ON,,,と動かしてもそれにはちょっと追いつけないのです。起動後はLowSpeedモードになっています(全部じゃないけどね)。動作速度を上げたいときはここを設定します

PUPDR

実は内部にプルアップ抵抗とプルダウン抵抗が入っています。それを有効化することが出来ます。

BSRR

ODRレジスタを操作すれば出力値は変えられます。
今回はLEDが1つですが、何個もついてくるとLEDをONにするときに間違ってほかをOFFにしないかなどちょっと気にしてしまいます。
BSRRはODRのセットリセットレジスタとなっていて、下位16bitはONにしたいbitを1にするとそこだけ1にしてくれて、0のbitは何も操作しないです。
また、上位16bitは0にしたいbitを1にするとそこだけ0にしてくれます。

LCKR

GPIOの設定をロックして間違ってプログラムで変えないようにするためのレジスタです

AFRLとAFRH

MODERでは4つのモードが選べましたが、AlternativeFunctionというのは、GPIOではない例えばUARTとかUSBとかそういった別の機能のことです。
それぞれのピンでAlternativeFunctionは16個選択できるようになっています。どれがなんなのかは既に決まっていて、好きに選ぶことは出来ないです。
例えば「USBはGPIOAの8がいい!」と思ってもGPIOAの8ではUSBは選択できないかもしれません。
それぞれのピンでどのAlternativeFunctionにするかはこのAFRLとAFRHで対応する場所を編集します。stm32f4_gpio_afr

AFRLとAFRHの2つに分かれているのは1つのピンで4bitもあるので2つのレジスタに分かれているだけです。

以上で全てです。これ以外にはありません。
GPIOでできることはこれだけ。
どんなに複雑そうに思えるシステムでもレジスタ以上のことは出来ません。
つまり、どんなレジスタがあるのだけ見れば何が出来るかがわかります。これは重要な事です

今回のまとめ

  • MODERにて入力モードにすればIDRレジスタにピンの状態が入る

また、GPIOのレジスタは

  • MODER ピンのモードを決める
  • ODR 出力値を決める
  • IDR 入力された値
  • OTYPER 出力タイプを選ぶ
  • OSPEEDR 速度を選ぶ
  • PURDR プルアップ・プルダウンを選ぶ
  • BSRR ODRのセットリセットレジスタ
  • LCKR このGPIOレジスタ全体を触らせないためのロック
  • AFRLとAFRH MODERでAlternativeFunctionを選んでいる時どのAFにするかの選択

 

次回は今後必要になってくるスタートアップスクリプトを見てみましょう。