Advent calendar revenge match in2020 (HAL+MPU9250)

これはMicro Mouse Advent Calendar 2020 - Adventarの2日目の記事です。 

昨日の記事は、なおフィスさんのMatlab/Simulinkでシミュレータ環境を作るコツ - なおフィスのブログでした。

当方はMATLABチュートリアルしかやってないのですがこんな便利機能あるとはしらなかっで今後の使用の際には参考にさせていただきます…

 

はじめましてジャッジーです。

今年は←まともに書いたので是非読んでいってください。

0.はじめのおしゃべり(飛ばしてどうぞ)

 昨年度12月にもマイクロマウスのAdvent calendarに呼ばせてもらったわけですが、

あの時ICM20689の実装などと題して"哺乳瓶でモンスターを飲むと不味くなる理由"について色々書きました。(は?)

結局ICM20689の実装は出来ずじまいでした(え?)

元々MPU6500での実装はsoraさんのブログ(STM32 + HALのSPIでMPU-6500と通信する | Sora's Activity Record)を見ながらやったことがあったので、同じInvensense社のICM20689はある程度のコピペで移植すれば実装は終わりだったのですが…授業が忙しくて結局それはやらないで終わりましたがね。ゆるさねぇぞ製図共々

          ↓

で、月日は流れて、今度こそは種類が違えど

ワイ「MPU9250を自分1人で実装までやりたいな〜(能天気顔)」

なんておこがましいことを思ってたんですよ(もちろん普通に無理ゲーだった)

 

で、まぁ少し詰まりつつも最終的に動かしたところに至るまでの流れを載せたいと思います。

ある程度Datasheetみたりは自分でやったりはしましたけども、

ハードウェアの注意点や生値の変換に際しては

しまじゃきさんやその他の先輩Nのお二方とお話ししてる中で教えてもらい、Soraさんのブログを一部参考にさせていただきました。感謝申し上げます。ありがとうございました。

 

そういえば、僕が最後にクラシックマウス関連のことをしたのがMPU6500の実装でした。フォルダみたら昨年の9月頃にMPU6500は完成してたみたいですね。もう一年以上も何もしないまま終わってました。(これ書いてるのは10月14日です)ははは。

 

実装に関してですが、今回大学でNucleo(STM32F401RE)を使う機会があったのでHALライブラリを用いて実装してみます。

f:id:WMMCsaiteihen:20201014035814j:image

1.使う物

・ジャンパー線♂♀

・ブレットボード

・ハンダゴテ(一部半田付けを行う)

・Nucleo(STM32F401RE)

・MPU9250ボード(千石にて購入)

MPU-9250搭載 IMUピッチ変換基板--販売終了 - スイッチサイエンス

 

2.SPI関して

  今回は昨年のAdvent calendarのリベンジなのでSPI通信での実装を予定してます。

SPI通信でマイコンペリフェラル間を繋ぐには

以下の配線が必要です。

f:id:WMMCsaiteihen:20201013192209j:image

MISOとMOSI、などの不思議な単語が並んでいますね。

MはMaster、SはSlave、 I&OはInput&Output

の頭文字ですね。最近はこの表現も問題視されて避けられるようですが、不便なので今回は使います。

各線の説明は以下のようなものです。

 

MISO:スレイブ(センサー)側からマスター(マイコン)へのデータを送信する線

MISOピンはSDOピンとも呼ばれる。

 

MOSI:マスター(マイコン)側からスレイブ(センサー)へのデータを送信する線

MOSIピンはSDIピンとも呼ばれる

 

CS:チップセレクター(スレイブ選択用)

どのセンサーと通信するか、を決めるピンです。上の図では省かれてますが、

SPI通信ではmaster1つに対してスレイブ側は複数使うことができ、以下のような配線になることがあります。

f:id:WMMCsaiteihen:20201013193945j:image

master側から見るとどのセンサーと通信すればいいのかわからないので、CSピンでどれにするかを決めるのです。

そしてCSピンですが、基本SPIの時は負論理です。

(つまり、通信する時以外は常にHighにしないといけない)

 

SCLK:通信タイミングを図る時計の役割です。

立ち上がりの時に通信するのか、それとも立ち下がりの時に通信するのか、などの設定が必要です。以下のCPOL、CPHAと言ったパラメータの設定を行うことでこれらの設定が可能となっております。

f:id:WMMCsaiteihen:20201013191208j:image

 

これで最低限のSPIの説明は大丈夫でしょう。

以上の画像の引用には以下のサイトから引用いたしました。

https://www.digikey.com/en/maker/projects/getting-started-with-stm32-how-to-use-spi/09eab3dfe74c4d0391aaaa99b0a8ee17

(2020年10月9日現在)

 

3.MPU9250に関して

 MPUシリーズはInvensense社から出ているIMUセンサーです。非常に多くの業界シェアを出していましたが今現在は製造廃止になってしまい、入手するには残された在庫のものを取り寄せる必要があります。(多分)

代わりに同じInvensense社から出ているICMシリーズが出ています。使い方もMPUシリーズと同じように使えるので今回書いたプログラムは移植性が高いですね。

まずはMPU9250のDatasheetを見てみます。

今回の実装に必要な

設定すること

必要なMPU9250レジスタ

 

を調べます。

Datasheetにはこちらのもの(https://invensense.tdk.com/wp-content/uploads/2015/02/PS-MPU-9250A-01-v1.1.pdf)を使いました。

以下にページ数とその画面を表示しながら何を読み取ったかを書いていくのでご一緒していただけると幸いです。(ここ以外にも読んでもらう必要はありますが、最低限の部分だけはここに載せておきます)

 

[p.5〜6]

まずp.5かp.6を開いてください

僕は英弱なのでp.5のことは軽く読んで

p.6のこちらで黄色の部分の確認をしました。

f:id:WMMCsaiteihen:20201014024620j:image

ここからわかることは

  • VDDには2.4〜3.6vを入れればよい
  • 1MHzなら全てのレジスタと会話できる

 

です。

 

[p.19]

次に19ページの使うピンのところを確認します

f:id:WMMCsaiteihen:20201014025018j:image

こちらですね。

黄色線で書いたところが今回必要なところです。

読み取ったこととして

  • ADO/SDO→MISO
  •  FSYNC→GND(僕これやり忘れてたんですけどうまくいきましたははは)
  • VDDに電源いれゆ
  • nCS→前述の CSピン
  • SCLK→前述のクロック用の線
  • SDA/SDI→前述のMOSI

 

でした。

 

[p.16]

では次に少し戻って16ページです。

f:id:WMMCsaiteihen:20201014030302j:image

これが今回の通信タイミングチャートです。

上のSCLKの説明の際、CPOLとかCPHAって言う単語を出したと思うんですが、その設定が読み取れます。上で挙げた例と見比べてみるとわかります。

ここから読み取れたこととして

  • 通信の際は CSピンはLow
  •  CPOL=1
  • CPHA=0(ただし2回目のedge時に通信)

 

が読み取れます。

 

[p.36]

最後に36ページに到達

f:id:WMMCsaiteihen:20201014030908j:image

こちらで読み取れることとしては

soraさんのブログと同様に

  • MSBが最初
  • 通信は16クロック
  • 読み取りか書き取りを決めるのは先頭の数字であり、0なら読み取り、1なら書き込み
  • その後の7bit分の数字で通信するレジスタのアドレスを示す

 

ということです。

これらの読み取ったことをきっちり設定すれば動くことがわかりました。

 

ここで、datasheetは一旦置いて、

register map(https://invensense.tdk.com/wp-content/uploads/2015/02/RM-MPU-9250A-00-v1.6.pdf)を覗きにいきましょう。

 

さて、レジスタマップを開けましたか?

さぁ見ていきましょう。ここから先は真っ暗で何も見えないですがね…(訳:マジで何もわからないまま進む)

と、言いつつここまで書いて疲れてきたのでザーッと見ていきましょう。

まず全体のレジスタマップで今回見るレジスタアドレスの部分を確認します

黄色の部分が今回必要なレジスタです。

面倒なので加速センサーについては端折ります。ジャイロセンサーのみやってみます。

f:id:WMMCsaiteihen:20201014034724j:image

f:id:WMMCsaiteihen:20201014034741j:image

f:id:WMMCsaiteihen:20201014034800j:image

 

[117. WHO_AM_I]

f:id:WMMCsaiteihen:20201014165213j:image

これがWHO_AM_Iのレジスタです。

これの用途は

This register is used to verify the identity of the devise.

と書かれています。つまり、通信しているスレイブがMPU9250であることを確認できる便利なレジスタです。

続きを読んでみると通信できていれば0x71という数字が返ってくる、ということを示しています。

読み取れたこととしては

  • レジスタ番号117(16進数で0x75)にアクセスして、返ってくる値が0x71なら通信できている

 

[26.CONFIG]

f:id:WMMCsaiteihen:20201014170959j:image

f:id:WMMCsaiteihen:20201014171010p:image

ページを跨いだので少しみにくいですが許してください…

これで設定するのはFIFO_MODEの設定です。

それ以外は0にしておけばいいんですが。

FIFO(メモリの一種)に関しては記載の通り追加のデータが来たら上書きしたいので0にします。

つまり0x00を書き込みます。

通常、FIFOは上書きを許可してない場合は

書き込もうとしても書き込むことすら不可能となります。

余談ですが、読み出しをやめるとその後書き込まない限り読み出す値は止める前のものになるんですが、僕の不勉強もあり、説明できないのでその辺の話はFIFOで調べてください。

 

読み取れることとして

  • レジスタ26(16進数で0x1A)にて、FIFOのデータ上書きするには0x00を書き込む

 

[27.GYRO_CONFIG]

f:id:WMMCsaiteihen:20201014171558j:image

ここでは、ジャイロの毎秒測れる角度範囲を指定するために3と4bit目を主に触ります。

今回は最高の2000dps(degree per second)のレンジに設定するため、

00011000→0x18をこのレジスタに書き込むことにします。

ちなみに、ここでどのレンジを選んだかによって、帰ってきた生値に対して行う処理が異なります。

これはレジスタマップよりDatasheetを見た方が手っ取り早いのでDatasheetの8ページをご覧ください。

以下の表が出てきます。

f:id:WMMCsaiteihen:20201014193229j:image

250→500→1000→2000と書いてある部分の下に

131→65.5→32.8→16.4

とかかれています。

これが変換に用いる値です。

 

今回は2000dpsを選択したので対応する16.4を使います。

角度=得られた生値/16.4

の変換式 で角度に変換します。

(この式が公式の書類では全然見つからない)

 

話は飛びましたが

  • GYRO_CONFIGには0x18を書き込む
  • 変換式にはDatasheetの8ページの値で除す

 

です。

 

[PWR_MGMT_1]

f:id:WMMCsaiteihen:20201014172121j:image

これは電源系統です。言葉の通り

パワーマネジメントですからね笑

ここでは下位の3bitでの設定を行い、

20MHzの内部クロックをオンにしておきます。

これ以外は使わないのGYRO_STANDBYは使うのかな…わからん(ので知見がある方は教えてください)

従って、

 

と言うことがわかりました。

ここまで長々とDatasheetやらRegister mapを追ってきました。

さてようやく設定することもわかりましたし

本格的な実装に参ります。

 

4.実装編

長ぇ…歴代で一番長いブログですねこれ。

さて、まずはハードウェアの準備から行きましょう。

4.1実装(ハード)

ハードウェアとしてはまず電源ラインがきちんと定格内であることがまず確認するべきことです。

今回はSTMマイコンを使いますので3.3V電源線があります。ここからVDDを持ってきましょう。

以下にNucleo(F401RE)

の中で今回使う部分のピンを示します。f:id:WMMCsaiteihen:20201014195329j:image

黄色の枠のところが今回使う部分です。

ほとんど全ての配線がここの枠内で完結します。

こちらが今回のMPU9250ボードです。配線は画像の通りの感じです。

青色のボードを使ってる人も多いですが、僕は赤色のでやります。

ここが最大の沼ポイントです

f:id:WMMCsaiteihen:20201014195414j:image

上の基板上の緑枠の部分、はんだ付けがなされてるんですが、ここのはんだ付けを切り替えておく必要があります。

よくわからないと思うので販売会社が出してる配線図を以下に示します。

https://cdn.sparkfun.com/datasheets/Sensors/IMU/SparkFun_MPU-9250_Breakout.pdf

 

f:id:WMMCsaiteihen:20201014195835j:image

上の赤丸。そうこいつ。

気づくわけもないがここの部分が示しているのは

  • ここのはんだ付けを反対側にくっつかないとSPIできないよー!

と言うもの。

声を大にしていいたい。

 

でかく書いておけ

 

はい。ここをはんだ付けしないとi2c通信しかできません。大事なことなのでもう一度言いますが

ここに気づかないと永遠にi2cしかされません。ここが沼です。底無し沼。

この記事を見た(ここまで読む暇人がいるのか?)人はどうか助かってほしい。

ここのはんだ付けさえ終われば以下のように配線してください。

 

f:id:WMMCsaiteihen:20201014200242j:image

 

これでハードウェアは終わりです。

画像には載せなかったですが、

回路にパスコンは入れておくべきでしょう。

最近の素子は精度の良いものが多く、パスコンを入れなくても誤動作やら発振やらしないものが多いですが、念のため入れておくといいです。(という受け売り)

 

電源はNucleoにプログラムを書き込むときにどうせパソコンとNucleoを繋ぐのでUSB経由でパソコン給電します。

 

4.2実装(ソフトウェア編)

長ぇ…がここさえ終わればあとは試してガッテン!です。(もしかして古い?)

もう疲れたんで、 Cube IDEの導入は言及しません…軽くこれの説明すると、STMマイコンへのプログラムを書くための便利ツールが丸ごと詰まったエディタです。

検索エディタでCube IDEで検索してみてください。

すぐST社の英語表記のホームページが出てくるのでそこでインストールできます。

また、シリアル通信画面を使うためにTeratermもインストールしておいてください。

 

さて、ここからはCube IDEがインストールできた人向けです。

まず、「プロジェクト」を作成し、ピン設定します。

 

ピン設定:

Cube IDEではピンの設定がGUIでできます。

レジスタ手打ち(GPIOA→…等)の構造体の中身を触ることなくピン設定できるのは人生の時間損失を10時間以上狭めます。(データシート見てたらマジで永遠にかかる)

f:id:WMMCsaiteihen:20201015112726j:image

GUIで右上の3ピンをそれぞれSPI3の

MOSI、MISO、SCLKにします。

それ以外に CSピンを設定します。

実際に触ってた人の中にはCSピン専用のピンがあることに気づいた人がいるかもしれません。あちゃ〜申し訳ないですがGPIO_outputに設定してもらえると助かります。

覚えていますか? CSピンは負論理で、Lowの時SPI通信が始まるって言う話がありましたよね?

GPIO_outputの方がLowにしやすくて簡単なのでCSピンとして使います。GPIOがわからない人がいたらアレなんで簡単に言っておくと

出力と入力のピンです。

 

あと、今回は角度の情報をシリアル通信してみたいのでUSART2用のピンが緑色(オンに)なってることを確認しておいてください。

 

さて次にそれぞれの詳細設定をしましょう。

ここでつまずく人は多いです。当の僕もよくわからんまま設定してます。

 

さぁ、始めましょう。

 

(1) SPI3の設定

まずは Clock Configilationを覗きに行きます。理由はSPI3に入るクロック周波数を知るためです。覚えていらっしゃらないかもしれないですが、MPU9250の全てのレジスタとお話しするには10MHzで通信する必要がありました。クロックの立ち上がり、立ち下がりに合わせて通信を行いますから、供給クロックは調べないとダメです。これを満たすためにどのようなプリスケーラー(分周)を与えれば良いのかを確認しに行きます。

__HAL_RCC_SPI_MspInitによれば、SPI3に供給するクロックはAPB1によるものですから

(↓これ見て分かった)

f:id:WMMCsaiteihen:20201015115034j:image
f:id:WMMCsaiteihen:20201015115031j:image

 

Clock Configilationの中でAPB1のクロック周波数を調べます。

すると42MHzと表記されていますね。

つまり、10MHzにするにはプリスケーラー(分周)を4に設定すれば大体10MHzです。(僕はお試し動作確認だったので16にしました)

出来るだけ早く通信したいのでやや無理やり10MHzを超えた値にしていますが、確実に動作させたいのでプリスケーラー(分周)を16にしてました。これであとは実際に設定するだけです。

設定場所として、 Connectivility→ SPI3と進んでいただき、parameter settingを選びます。以下の画像のように設定を進めます。

f:id:WMMCsaiteihen:20201015112708j:image

まず一番上の部分ですが、

Full duplex masterと設定します。

Full duplex とHalf duplexがありますが、何が違うのでしょう。

これは通信に使う線の本数です。今回、送信と受信に1本ずつ、計2本の通信線を用意してます。このように送信、受信に1本ずつ通信線が設けられている時はFull duplex です。

送受信まとめて1本の時はHalf duplexです。

追記:(と言う解釈をしてたのですがどうやら間違いのようでした)(2020,12/01)

 

その次のHardware NSS Signalはdisableに設定します。(今回ソフトウェアで CSピンをLowにするので)

MPU9250のデータはMSB firstでした。こちらはデフォルトで設定されてるはずですが確認しておきましょう。

プリスケーラーを4or16に設定し、

先述した CPOLとCPHAの設定です。

だいぶ上の方で説明したので忘れてる人もいるかと思いますが、

 CPOL→High

CPHA→2 Edge

に設定してください。なんでこの設定になったかは上の方で説明したので省きます。

多分後の残りはデフォルト設定でOKなので放置です。

 

(2)USART2の設定

次にUSART2の設定です。USART2もconnectivity→USART2で設定箇所に飛べます。

USART2で設定するべきはbaud rateですね。

僕はTeratermのデフォルト設定が115200なので今回も115200に定めます。

f:id:WMMCsaiteihen:20201015115347j:image

注)Teratermのデフォルトbaud rate設定は9600なので通信すると文字化けします。

設定を変えるには上のバーの中で設定→シリアルポートで変更できます。

f:id:WMMCsaiteihen:20201015113723j:image

f:id:WMMCsaiteihen:20201015113726j:image

 

USART2に関してはこれで設定自体は終わりです。簡単ですね。

 

コーディング:

基本的なコードはSoraさんのもの(STM32 + HALのSPIでMPU-6500と通信する | Sora's Activity Record)と同じでした。

勉強のためにほぼ見ないで書いたんですけどCSピンをLowにする処理の位置が違うこととか、HAL_Delayを入れてるか否か程度の違いでしたので(あと僕のは加速度系への書き込みとかがある)ソースコードは載せません。

Soraさんのものを見ていただければとりあえずよろしいかと思います。

もしそのうち気が向いたら追記という形で載せるかもです。

 

結果

コンパイルして書き込んでやると、

Teraterm上で左右に振った時の加速度が検知できました。

f:id:WMMCsaiteihen:20201130042252j:image

 

 

長々と書きましたが

明日は…明日は…?誰もいなぃ…虚無…もぅむり…リスカして死のぅ…