STM32でCMSIS DSPを使ってみる その1
今回は高速に三角関数とかを計算できるARM社提供ライブラリであるCMSIS DSPライブラリを使ってみる話です。
こいつを使うとsin,cosとかよく使う三角関数やaddとかを高速に処理できます。
そのほかFIR,IIRと言ったデジタルフィルタ用のAPIだったり制御用のAPIもあるのでつかわない手はないです。
導入
今回の実習環境は以下の通りです
- IDE:AtollicTrueStudio
- Board:STM32F767 Nucleo144
CubeMXで適当にプロジェクトを作ってビルドが通るとこまで作っておきます。
スタティックライブラリの追加
次にコンパイル済みのライブラリを追加していきます。
オブジェクト自体はCubeF7のフォルダ内にあります。
場所を調べるには
CubeMX上記メニューからhelp->UpdaterSettingでパスをコピーします

ファイルエクスプローラーでそこに移動します。
するとこんな感じのファイル構成になっていると思います

オブジェクトは以下のPATHにあります。
STM32Cube\Repository\STM32Cube_FW_F7_V1.6.0\Drivers\CMSIS\Lib

三つありますがダブルポイント(倍精度)かシングル(単精度)かを選ぶ。
今回はF767なのでdpがついてるlibarm_cortexM7lfdp_math.aを選びます。
それをコピーしてプロジェクト直下にいれます。

プロジェクトの設定
プロジェクトのプロパティーを開く
「C/C++Build」/GCC linker/Miscellaneous欄で
Linker、LibrariesでDSP処理のLinkerとかを設定していく
Librariesに下記を追加。
arm_cortexM7lfdp_math.a
ものによってはエラーになるので.aをなくした版で試して見てください
arm_cortexM7lfdp_math
コードを書いていく
ヘッダーをインクルードします。
#include "arm_math.h"
今回は試しに平方根でもやってみます。
FastMathのとこに入ってます。
CMSIS DSP研究室さんで詳しく解説されています。
APIとしては
arm_sqrt_f32 (float32_t in,float32_t * pOut )
を用います
こんな感じに書いた

まとめ
- 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が提供するサンプルから引っ張ってくるという方法もあります。

で今回使うボードを選択

CubeHALを選択

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


んで一度コンパイルします。
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というのを追加するだけです。

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

結構サイズがでかくなります。
いうてGCCならKEILと違って32KB以上もビルドできるしFloat扱うようなマイコンはフラッシュ自体大きい物がのってるので特に悩まないかと思います。
ARMのソフトウエアトリガ割り込みについて
ARMのソフトウエア駆動割り込みの覚え書きです。
ARMの割り込みはNVIC(統合ネスト型ベクタ割り込みコントローラ)で管理されていますがその中でSTIRというレジスタがあります

図で言うと赤の部分ですね
詳細はこちらになります

Write to the STIR to generate an interrupt from software
とあるように割り込みの番号を入れてやるとソフトウエアから割り込みを生成できるようです。
実際の使い方はこのような感じです。

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

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

図ではわからないですがスイッチを押さなくても割り込みハンドラに遷移しています。
もちろん割り込みも有効なのでスイッチを押すとそのタイミングでも割り込みがかかります。
なのでこの割り込みを使う際は使わないであろうペリフェラルを殺しておいて使うほうがいいでしょう
STM32でI2C通信をやってみる
この記事は現在一緒に開発やってくれてるオカダ
twitter.com
と共同でデバッグしてた時の記録です。
一応動いていますがHALのバグなのか正しい使い方なのかは不明です。
やる際は自己責任でお願いします。
とりあえずやってく
今回は二つのマイコンで通信するので二つNucleoを用意しないといけません。
Slave側の設定
CubeMXを立ち上げてI2Cを有効にする。
クロックなどは各自自由に設定してください
I2Cの設定はこんな感じにした。

アドレスは7bitモードと10bitモードがあるが今回は7bitモードにした。
アドレス値が0だがあとでコード内で記述するのでここでは放置
次にSW4STM32にインポートしていく。
ここでI2Cのアドレス設定する。
hi2c1.Init.OwnAddress1 = 0x01<<1;

ここでシフト演算子でアドレスをシフトしているのは後述するのでとりあえずこうしてください。
次に受信処理を書いていきます。
HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_I2C_Slave_Receive(&hi2c1,(uint8_t*)aRxBuffer, 1, 1000);
こんな感じに書きます。
aRxBufferは上のほうでこんな感じに定義してます。
uint8_t aRxBuffer[1] = {0};
無限ループ内はこんな感じになります

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

今回はマスターなのでアドレスはノーケアでOK
AC6に読み込んで送信コードを書いていく
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
が送信APIなのでそれを使って書いていく。
ここで注意するのがAddressをシフトして引数に入れることだ

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

ここでは上部に何も記述してないが実際の動作コードでは送信バッファaTXBufferに0xAB、0xBAを順番に代入し交互に送られるようにした。
問題のシフトの説明
上記の例は動作するケースだけを示している。
それはシフト演算子でアドレスをシフトしているのできちんと読みこめています。
それについて最初まったく原因がわからず時間を溶かしました・・・・・・・
結局
CubeMXが出力するコードに問題があり自身のアドレスを示すレジスタの値がおかしいことになっていた。
設定レジスタはI2C_OAR1のOA1ビット(7:1bit)である

そこを見てみると


上記のように自身のアドレスをシフトして入れないとレジスタに正しく値がセットされていない。
CubeMX上でPrimaryアドレスにそのままの値を入れてもこうなってしまう
正しくはシフトがついた状態で出力されているもしくはHALの内部でシフトしてセットしているはずである。
よって使い方が悪いわけではなさそうなので一応バグに入るのかね?
これについては海外フォーラムでも話題になっていたみたいである。
先に読めばよかった・・・・・・・・
今回はこんな感じです
皆さんのお役に立てたらうれしいです。それでは!
STM32のMCO機能を使ってGPIOからクロックを出してみる
MCO機能
マイクロコントローラクロック出力(MCO)機能では、外部 MCO ピンにクロックを出力することができます。
5 つのクロック信号のうちの 1 つを MCO クロックとして選択できる。
実際の使用例ではNucleoなんかがそうですね、
STLINKのF103からボード上のMCUへクロックを供給してます。
MCOへ供給可能なクロックは以下の通りだ
選択はクロック設定レジスタ(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を分周できる

レジスタの説明
主にRCC_CFGRで設定する

追記
ここでMCOFビットはSTM32F303xB/C and STM32F358xC onlyとあるので30:28がMCOPREとなる。
英語版最新データシートより

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を有効する

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

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

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

おまけで500kHzも出してみた
128分周して500kHz出せたぞー
— がれすた (@GarageStyleMsc) 2017年3月31日
なのでメインクロックは64MHz
ちゃんとSystemCoreClockの値があってるかも確認できたし満足 pic.twitter.com/yxatNHt1jo
まとめ
- MCOを使ってクロック出力ができた
- MCO機能で他のマイコンにクロック供給できるので今度やってみたいと思った
STM32でUARTをやってみる5(MicroShell
こんかいもUARTのネタです
中村さん作のMicroShellの話でございます。
実を言うと中村さんには一度SignalBottomでお会いしたことがあります。
その時は中村さんの作品を一つしか使ったことなかったので次回お会いできる機会があれば聞いて見たいことをいっぱい聞いて見たいと思います。
本人blogは以下のリンクから
本題
簡単に説明すると組み込み用Shellです。
導入はLPCのサンプルプロジェクトが入ってるのでEclipseプロジェクトでの導入は楽だろう
しいて言えば
- uart_getc
- uart_putc
- uart_puts
のAPIがSTMにないのでラッピングして作るのが結構めんどかったりする。
とりあえずlibファイル一式インポートして

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

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

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

タスクの追加方法
上部でタスクを定義

コマンドテーブルに追加

実際の処理部分を追加











