STM32でI2C通信をやってみる

この記事は現在一緒に開発やってくれてるオカダ
twitter.com
と共同でデバッグしてた時の記録です。

一応動いていますがHALのバグなのか正しい使い方なのかは不明です。

やる際は自己責任でお願いします。


とりあえずやってく

今回は二つのマイコンで通信するので二つNucleoを用意しないといけません。

Slave側の設定

CubeMXを立ち上げてI2Cを有効にする。
クロックなどは各自自由に設定してください

I2Cの設定はこんな感じにした。

f:id:gsmcustomeffects:20170403011226p:plain

アドレスは7bitモードと10bitモードがあるが今回は7bitモードにした。
アドレス値が0だがあとでコード内で記述するのでここでは放置

次にSW4STM32にインポートしていく。

ここでI2Cのアドレス設定する。

hi2c1.Init.OwnAddress1 = 0x01<<1;

f:id:gsmcustomeffects:20170403013426p:plain

ここでシフト演算子でアドレスをシフトしているのは後述するのでとりあえずこうしてください。

次に受信処理を書いていきます。

HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout)

というAPIブロッキング受信なのでそれを使っていきます。

HAL_I2C_Slave_Receive(&hi2c1,(uint8_t*)aRxBuffer, 1, 1000);

こんな感じに書きます。
aRxBufferは上のほうでこんな感じに定義してます。

uint8_t aRxBuffer[1] = {0};

無限ループ内はこんな感じになります

f:id:gsmcustomeffects:20170403015132p:plain

Master側の設定

上記と同様にCubeMXの設定をする。

f:id:gsmcustomeffects:20170403015318p:plain

今回はマスターなのでアドレスはノーケアでOK

AC6に読み込んで送信コードを書いていく

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)

が送信APIなのでそれを使って書いていく。

ここで注意するのがAddressをシフトして引数に入れることだ

f:id:gsmcustomeffects:20170403022048p:plain

んでそれにならって書くとこんな感じ

f:id:gsmcustomeffects:20170403015515p:plain

ここでは上部に何も記述してないが実際の動作コードでは送信バッファaTXBufferに0xAB、0xBAを順番に代入し交互に送られるようにした。

実際に動作させていく

両方のI2CピンとGNDピンをつなぎバス間部分をロジアナでみてみる

f:id:gsmcustomeffects:20170403015919p:plain

きちんと交互にデータが来ていることがわかる。

ちなみにその時の送受信レジスタの値はこのようになっている。

f:id:gsmcustomeffects:20170403020702p:plain

問題のシフトの説明

上記の例は動作するケースだけを示している。

それはシフト演算子でアドレスをシフトしているのできちんと読みこめています。
それについて最初まったく原因がわからず時間を溶かしました・・・・・・・

結局

CubeMXが出力するコードに問題があり自身のアドレスを示すレジスタの値がおかしいことになっていた。

設定レジスタはI2C_OAR1のOA1ビット(7:1bit)である

f:id:gsmcustomeffects:20170403022913p:plain

そこを見てみると

f:id:gsmcustomeffects:20170403021443p:plain

f:id:gsmcustomeffects:20170403021512p:plain

上記のように自身のアドレスをシフトして入れないとレジスタに正しく値がセットされていない。
CubeMX上でPrimaryアドレスにそのままの値を入れてもこうなってしまう

正しくはシフトがついた状態で出力されているもしくはHALの内部でシフトしてセットしているはずである。

よって使い方が悪いわけではなさそうなので一応バグに入るのかね?

これについては海外フォーラムでも話題になっていたみたいである。
先に読めばよかった・・・・・・・・

community.st.com

今回はこんな感じです

皆さんのお役に立てたらうれしいです。それでは!


STM32のMCO機能を使ってGPIOからクロックを出してみる

MCO機能

マイクロコントローラクロック出力(MCO)機能では、外部 MCO ピンにクロックを出力することができます。

5 つのクロック信号のうちの 1 つを MCO クロックとして選択できる。

実際の使用例ではNucleoなんかがそうですね、
STLINKのF103からボード上のMCUへクロックを供給してます。

MCOへ供給可能なクロックは以下の通りだ

  • LSI
  • LSE
  • SYSCLK
  • HSI
  • HSE
  • 2 分周された PLL クロック

選択はクロック設定レジスタRCC_CFGR)の MCO[2:0] ビットによって制御できる。
STM32F303xD/E、STM32F303x6/8 および STM32F328x8、 では、このレジスタの追加ビット
PLLNODIV が、MCO への PLL クロック入力の分周バイパスを制御します。
MCO 周波数は、クロック設定レジスタRCC_CFGR)の MCOPRE[2..0] ビットで制御される設定可能な分周器によって低減できます。

とあるがこれをまとめると

  • RCC_CFGRのMCOビットでクロックの元を選べる
  • STM32F303xD/E、STM32F303x6/8 および STM32F328x8はプリスケーラを持っているので分周できる
  • PLLNODIVを設定することでPLLCLKを使う場合分周の有無を選べる
  • RCC_CFGRのMCOPREビットでMCOを分周できる

f:id:gsmcustomeffects:20170401174659p:plain

レジスタの説明

主にRCC_CFGRで設定する
f:id:gsmcustomeffects:20170401180911p:plain
追記
ここでMCOFビットはSTM32F303xB/C and STM32F358xC onlyとあるので30:28がMCOPREとなる。
英語版最新データシートより
f:id:gsmcustomeffects:20170401220727p:plain

PLLNODIV

bit 機能
0 MCOの前でPLLを2分周
1 MCOの前でPLLは分周されない

MCOPRE[2:0]

bit 機能
000 MCO を1分周
001 MCO を2分周
010 MCO を4分周
011 MCO を8分周
100 MCO を16分周
101 MCO を32分周
110 MCO を64分周
111 MCO を128分周

MCO[2:0]

bit 機能
000 出力無効、MCO にクロックなし
001 予約済み
010 LSI クロックの選択
011 LSE クロックの選択
100 システムクロック(SYSCLK)の選択
101 HSI クロックの選択
110 HSE クロックの選択
111 PLL クロックの選択(PLLNODIV ビットに応じて 1 または 2 で分周)

CubeMXでのやり方

RCC画面でMCOを有効する
f:id:gsmcustomeffects:20170401181248p:plain

クロック設定画面で好きなように設定してみる
今回はSYSCLKを選んで8分周するので8MHzが出てくるはずである

f:id:gsmcustomeffects:20170401181509p:plain

あとは出力してビルドするだけ
今回はNucleoF303kを使っているのでD9ピンからクロックが出ているはずだ。

f:id:gsmcustomeffects:20170401181823p:plain

8分周したMCOから8Mhzが出てきていることから64MHzがSYSCLKということになる
SystemCoreClockは64MHzなのできちんとこの周波数で動いていることがわかる

f:id:gsmcustomeffects:20170401182118p:plain

おまけで500kHzも出してみた



まとめ

  • MCOを使ってクロック出力ができた
  • MCO機能で他のマイコンにクロック供給できるので今度やってみたいと思った

STM32でUARTをやってみる5(MicroShell

こんかいもUARTのネタです

中村さん作のMicroShellの話でございます。

実を言うと中村さんには一度SignalBottomでお会いしたことがあります。
その時は中村さんの作品を一つしか使ったことなかったので次回お会いできる機会があれば聞いて見たいことをいっぱい聞いて見たいと思います。

本人blogは以下のリンクから

shinta-main-jp.blogspot.jp

本題

簡単に説明すると組み込み用Shellです。

導入はLPCのサンプルプロジェクトが入ってるのでEclipseプロジェクトでの導入は楽だろう
しいて言えば

  • uart_getc
  • uart_putc
  • uart_puts

APIがSTMにないのでラッピングして作るのが結構めんどかったりする。

とりあえずlibファイル一式インポートして
f:id:gsmcustomeffects:20170324025400p:plain

指示通りAPIをパースする。

f:id:gsmcustomeffects:20170324025656p:plain

main.cの無限ループ内で以下の記述をする

f:id:gsmcustomeffects:20170324025938p:plain

あとは好きにコマンドなりをサンプルと同じように追加していく

f:id:gsmcustomeffects:20170324030112p:plain

Teratermで確認

適当にコマンド追加してLEDトグルしてみた

f:id:gsmcustomeffects:20170324030243p:plain

タスクの追加方法

上部でタスクを定義

f:id:gsmcustomeffects:20170417200057p:plain

コマンドテーブルに追加

f:id:gsmcustomeffects:20170417200203p:plain

実際の処理部分を追加

f:id:gsmcustomeffects:20170417200345p:plain

まとめ

いやぁ凄いの一言

ここまで簡単に使えてコードサイズが10KB程度って凄すぎでしょ・・・・・・・・
F030で普通に使えるし

コマンド追加も結構楽

あとはデバッグ途中関数ポインタを使った簡単なプログラムでつまずいてリアルテック氏にご教授いただいたのでもっと勉強します。
ポインタできないと彼女できないらしいので・・・・・

STM32でUARTをやってみる4(Chanさんのxprintf)

今回はやっとこの話題を書いていく。

  • uart_getc
  • uart_putc
  • uart_puts

が必要なのでそれを読んでほしい

gsmcustomeffects.hatenablog.com

Xprintf

組み込み用printfモジュールである。
まあ組み込みだと素のprintfが使えないことが多いしましてやUARTに吐くとなるといろいろな制約がある。
そこでFatFsで有名なChan氏が作ってくれたのである。

詳しくは以下参照

ELM - 組み込み用printfモジュール

導入

ダウンロードされた

  • xprintf.h
  • xprintf.c

をプロジェクトにインポートする。

ファイルからコピーしてペーストするとこういうふうに入る

f:id:gsmcustomeffects:20170324022100p:plain

んでmain.cでインクルード

f:id:gsmcustomeffects:20170324022210p:plain

使ってみる

使い方は結構簡単で上記の送信関数uart_putcをxdev_outマクロに投げるだけだ。
ポインタ形式じゃないのはChanさんが丁寧にマクロ化してくれてるからです

f:id:gsmcustomeffects:20170324022518p:plain

APIは普通のprintfと同じ(xprintfだけど
ちなみに浮動小数点は非対応

f:id:gsmcustomeffects:20170324023134p:plain

Teratermで確認

こんな感じになってればOK

f:id:gsmcustomeffects:20170324023211p:plain

まとめ

今回はUARTのちょっとした利用例とのことでprintfを作ってみたがSTのsampleにも似たようなものがあった気がする(あれば謎

でも汎用モジュールなのでやはりCHANさんが最強w

STM32でUARTをやってみる3

前回と前々回でUARTのくっそ簡単な送受信について書いたんだけど今回はグローバル変数いちいち作るのダサいしそんなのあんましつかわんよね見たいな感じだったので
LPCマイコンAPIで用意されてるやつをSTMでも作ってみようということで

  • uart_getc
  • uart_putc
  • uart_puts

的なのを作る。


作るとはいっても送信APIと受信APIをラップするだけ(おい~

f:id:gsmcustomeffects:20170323125050p:plain

まあ適当につくったのでこんな感じ

これがあるとこんな感じで文字列型で投げれるのでちょいちょいデバッグしたいときに便利
まあ最近はデバッガで変数の中の値を見れるので組み込み用ミニprintfなんかを作らなくてもいいかななんて思うこともしばしば。

f:id:gsmcustomeffects:20170323125329p:plain

まとめ

実際これはPC→MCUみたいな時の関数なので

MCUMCUなんかだとなんかしら決まりを作ってマルチバイトで送るのがベストというかあとあとbitシフトで取り出しやすいというかんじ。

ascii形式を使うのはTeratermでみれるからというのが一番の理由かな

本来ならバイナリ形式だしね

次回はCHANさんのxprintfでもやってみます。

STM32でUARTをやってみる2

はじめに

前回はすごく簡単なUARTのHelloWorldをやったので今回は受信とかやってみようと思う。

ちなみに前回の記事はコレ

gsmcustomeffects.hatenablog.com

CubeMXの設定は前回と同じ

f:id:gsmcustomeffects:20170322123624p:plain

f:id:gsmcustomeffects:20170322123737p:plain

f:id:gsmcustomeffects:20170322123804p:plain

Ac6での作業

今回やること

  • 'a'を受け取ったらボード上のLEDが点灯
  • 'b'を受け取ったらボード上のLEDが消灯
  • それ以外だとUARTでOther Key Pressedの文字を返す

前回は送信だけだったが受信は受け取り側のバッファ確保が必要となるのでそれっぽい配列を確保しとく


僕はこの辺に追加しました
f:id:gsmcustomeffects:20170323114506p:plain

UserCodeBeginとかの間にコード書いとくとCubeで再度出力したときに消えないので便利です。

んであとは無限ループ内でコードを書いていくだけ今回は受信APIを使うのでそれについて少し

HAL_UART_Receive(&huart2,(uint8_t *)rxbuf, sizeof(rxbuf), 0xF);

ほとんど送信APIと使い方は同じだが、最後のタイムアウト時間で送信の場合自発行為なのでタイムアウトすることがほとんどない。
受信の場合向こうからくるまでプログラムが止まるので注意しないといけない。
特に何も考えずにFFFFとかにするとめっちゃ止まる。

基本この辺は割り込みでレジスタ見に行くのでUARTを割り込みでさばけるならそうしたほうがいいかもしれない。

んで肝心なくそコードがこんなかんじ

f:id:gsmcustomeffects:20170323115415p:plain

実際の動作確認

Teratermだと

f:id:gsmcustomeffects:20170323115609p:plain

実際のボード

f:id:gsmcustomeffects:20170323115637j:plain

eclipseのbreak

ちゃんとaが格納されてる

f:id:gsmcustomeffects:20170323115931p:plain