前回はマイコンのピンの電圧制御にはGPIOペリフェラルをいじれば良いということがわかりました。
ODRレジスタの対応するビットを操作することでONOFFしていましたが、今回は逆に入力をやってみましょう。
入力とは
STM32F4Discoverにはボタンが2つあり、うち1つは自由に使うことが出来ます。
この青いボタンがそうです。このボタンがマイコンのポートAの0につながっています。
回路図としてはこんな感じです。
ボタンが押されていない時はPA0は220kΩでGNDに繋がれ0Vになり、押されるとVDDに繋がり3.3Vになる(VDDは3.3Vです)感じです。
それがGPIOのポートAの0につながっています。
GPIOは1か0かでピンの電圧を3.3Vか0Vかにプログラムから切り替えられましたが、逆に高いか低いかを1か0かで入力する機能もあります。
MODERをもう一度見てみる
ここで、前回出てきたGPIOのMODERレジスタをもう一度見てみましょう。
前回はこれを元に青色LEDが繋がっている場所を10(出力モード)に切り替えました。これによると
- 00 入力(reset state)
- 01 出力
- 10 AlternativeFunction
- 11 アナログ
となっています。実は元々GPIOは全てのピンが最初から入力モードになっていて、ピンの状態をプログラムから読み取れる状態になっています。
では、読み取った値は出力を決めるのに使っていたODRに入るのかというと、そうではなく別の今度はIDRというレジスタに入ります。
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目に挿入されます。
もちろん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かを出力するというものです。普通ですね。
オープンドレインというのはトランジスタ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で対応する場所を編集します。
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にするかの選択
次回は今後必要になってくるスタートアップスクリプトを見てみましょう。