STM32でCMSIS DSPを使ってみる その1

今回は高速に三角関数とかを計算できるARM社提供ライブラリであるCMSIS DSPライブラリを使ってみる話です。
こいつを使うとsin,cosとかよく使う三角関数やaddとかを高速に処理できます。
そのほかFIR,IIRと言ったデジタルフィルタ用のAPIだったり制御用のAPIもあるのでつかわない手はないです。

導入

今回の実習環境は以下の通りです

  • IDE:AtollicTrueStudio
  • Board:STM32F767 Nucleo144

CubeMXで適当にプロジェクトを作ってビルドが通るとこまで作っておきます。

スタティックライブラリの追加

次にコンパイル済みのライブラリを追加していきます。
オブジェクト自体はCubeF7のフォルダ内にあります。

場所を調べるには

CubeMX上記メニューからhelp->UpdaterSettingでパスをコピーします

f:id:gsmcustomeffects:20170405164420p:plain

ファイルエクスプローラーでそこに移動します。

するとこんな感じのファイル構成になっていると思います

f:id:gsmcustomeffects:20170405164606p:plain

オブジェクトは以下のPATHにあります。

STM32Cube\Repository\STM32Cube_FW_F7_V1.6.0\Drivers\CMSIS\Lib

f:id:gsmcustomeffects:20170405165503p:plain

三つありますがダブルポイント(倍精度)かシングル(単精度)かを選ぶ。
今回はF767なのでdpがついてるlibarm_cortexM7lfdp_math.aを選びます。

それをコピーしてプロジェクト直下にいれます。

f:id:gsmcustomeffects:20170405164940p:plain

プロジェクトの設定

プロジェクトのプロパティーを開く
C/C++Build」/GCC linker/Miscellaneous欄で
Linker、LibrariesでDSP処理のLinkerとかを設定していく

Librariesに下記を追加。

arm_cortexM7lfdp_math.a

ものによってはエラーになるので.aをなくした版で試して見てください

arm_cortexM7lfdp_math

Library search pathに下記を追加

"${workspace_loc:/${ProjName}}"

上記の例では.aファイルは直下にあるのでこのように書いている。
ちなみに$workspace_locとかはeclipseディレクトリ変数的なやつなのでeclipseのドキュメントを確認するといろいろのってる。

プリプロセッサの設定

__FPU_PRESENT
ARM_MATH_CM7

f:id:gsmcustomeffects:20171209153546p:plain

を追加する。

FPUの設定

倍精度の奴を選びます。
詳しい説明はここ読んでください
ARM Information Center

f:id:gsmcustomeffects:20170405165752p:plain

んでいったんビルドする

f:id:gsmcustomeffects:20170405165819p:plain

コードを書いていく

ヘッダーをインクルードします。

#include "arm_math.h"

今回は試しに平方根でもやってみます。
FastMathのとこに入ってます。

CMSIS DSP研究室さんで詳しく解説されています。

DIGITALFILTER.COM

APIとしては

arm_sqrt_f32 (float32_t in,float32_t * pOut )

を用います

f:id:gsmcustomeffects:20170405170921p:plain
Square Root

こんな感じに書いた
f:id:gsmcustomeffects:20170405171946p:plain

動作

f:id:gsmcustomeffects:20170405172020p:plain

入力した値の平方根が帰ってきているのでちゃんと動いているといえる。

ちなみに返り値でStatを見ているがそれの返り値はENUMで定義されてる。

f:id:gsmcustomeffects:20170405172126p:plain

まとめ

  • DSPライブラリを導入できた
  • FastMath使った割に速度については検討してないので今後やって行きたい

STM32でUARTをやってみる6(float型printfをUART経由で出力)

何だかんだこのシリーズも6個目ですねw

前回まででUARTの基本的な使い方はマスターしたと思います。
今回はCubeMXでの出力は詳しくは解説しないので

まだの方は1から読んでみてください

gsmcustomeffects.hatenablog.com

gsmcustomeffects.hatenablog.com

gsmcustomeffects.hatenablog.com

gsmcustomeffects.hatenablog.com

gsmcustomeffects.hatenablog.com

さっそくやってく

今回の環境は以下に示す通り

  • AC6 SystemWorkbenchForSTM32
  • STM32F303K Nucleo
  • Teraterm

とりあえずCubeでUART2を有効にしてボーレートを各自設定しファイルを出力する。
ココまでは今までと同じです。

Syscall.cを持ってくる

syscall.cがCにおけるスタンダードライブラリを読んでいるのでそれをどっかからもってこないとリンクできないわけです。
とりあえずSTが提供するサンプルには入っているのですがCubeMXが出力するsrcフォルダには入っていません。

なのでAc6で適当な空プロジェクトを作る必要があります。
めんどかったらSTが提供するサンプルから引っ張ってくるという方法もあります。

f:id:gsmcustomeffects:20170404195559p:plain

で今回使うボードを選択

f:id:gsmcustomeffects:20170404195630p:plain

CubeHALを選択

f:id:gsmcustomeffects:20170404195658p:plain

そうするとsyscall.cができるのでそれをCubeMXが出力したSrcにコピーしてくる

f:id:gsmcustomeffects:20170404195814p:plain

f:id:gsmcustomeffects:20170404195841p:plain

んで一度コンパイルします。


Main.cでputcharを再定義。

syscall.c内では

__io_putchar(int ch) __attribute__((weak))

のようにWEAK定義されているのでMain内で再定義してこっちを使うよということをコンパイラに教えてあげる作業をします

こういうふうにします。

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
void __io_putchar(uint8_t ch) {
HAL_UART_Transmit(&huart2, &ch, 1, 1);
}

printfというのは内部で書式を整えてputcharを呼び出しているのでそのputcharの内部をHALの一文字送信APIに置き換えるということです。

コンパイルマクロがGNUとかついてるのはARMの純正コンパイラではfputcと定義されてるのでコンパイラごとに読み替えが効くようにしているためです。

次にprintfを呼ぶ前にどこか最初以下の行を挿入します

setbuf(stdout, NULL);

これはprintfの仕様みたいで1024byte入れた後に呼ばれるのでコレがないと変なハンドラに飛びます。
とりあえずバッファをフラッシュするといいみたいです。

ここまででとりあえずint型のprintfは動きます。

だいたい15KBぐらいになります。

Linkerフラグの編集

リンカーオプションにfloatを追加して再ビルドする必要があります。

やることとしてはlinkerのフラグ部分に-u _printf_floatというのを追加するだけです。

f:id:gsmcustomeffects:20170404201644p:plain

それであとはApplyして再ビルドします。

f:id:gsmcustomeffects:20170404202009p:plain

結構サイズがでかくなります。

いうてGCCならKEILと違って32KB以上もビルドできるしFloat扱うようなマイコンはフラッシュ自体大きい物がのってるので特に悩まないかと思います。

動作のようす

こんな感じに書いて

f:id:gsmcustomeffects:20170404202315p:plain

Teratermで表示する

f:id:gsmcustomeffects:20170404202339p:plain

ちなみに桁数ちゃんと合わせないとゴミが入りますw

ちなみにSemihostingというARMの機能を使ってSWD経由でEclipse上のコンソールに出力することもできます。
それについてはユークリッドさんが書いているので引用させていただきます。

yuqlid.hatenablog.com

yuqlid.hatenablog.com

場合分けで使いたい方使えばいいと思います。

単に値見たいだけならExpression機能もありますしね

ARMのソフトウエアトリガ割り込みについて

ARMのソフトウエア駆動割り込みの覚え書きです。

ARMの割り込みはNVIC(統合ネスト型ベクタ割り込みコントローラ)で管理されていますがその中でSTIRというレジスタがあります

f:id:gsmcustomeffects:20170404051828p:plain

図で言うと赤の部分ですね

詳細はこちらになります

f:id:gsmcustomeffects:20170404052003p:plain

Write to the STIR to generate an interrupt from software

とあるように割り込みの番号を入れてやるとソフトウエアから割り込みを生成できるようです。

実際の使い方はこのような感じです。

f:id:gsmcustomeffects:20170404052313p:plain

STIRレジスタIRQ番号を書くだけです

STM32の場合IRQ番号はstm32f767xx.hに定義されています

f:id:gsmcustomeffects:20170404052432p:plain

ためしにピン割り込みハンドラにソフトウエアから移動してみます

f:id:gsmcustomeffects:20170404052528p:plain

図ではわからないですがスイッチを押さなくても割り込みハンドラに遷移しています。
もちろん割り込みも有効なのでスイッチを押すとそのタイミングでも割り込みがかかります。
なのでこの割り込みを使う際は使わないであろうペリフェラルを殺しておいて使うほうがいいでしょう

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で普通に使えるし

コマンド追加も結構楽

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