2019年12月21日

STM32マイコン UART受信時必須処理

STM32マイコンでUARTの受信をする際に必須となるエラー処理について紹介します。

UARTの送信は一方的にマイコンから送るだけのため、そこまで大変ではありません。一方、受信は調歩同期方式ならではのエラー(フレーミング、バッファ オーバーラン エラー等)が発生するため、エラー処理を入れないとUARTの受信処理の安定性が悪化します。

更に厄介なのがSTM32マイコンのHALライブラリではUARTの受信割込みやDMA転送を使用した際に受信エラーが発生すると以降の受信処理が行われません。コマンドをUARTで処理する場合、受信エラー以降の受信が行われず、応答なし状態となっていまいます。

受信処理を継続して行うためにはエラー処理を加える必要があります。



実装としてはエラーが発生するとHAL_UART_ErrorCallbackが呼び出されます。
※設定によりますが、HAL_UART_ErrorCallbackが呼び出されないエラーがあったため、HAL_UART_ErrorCallback内でクリアせずにwhile内等でのクリア処理に変更しました。

UARTのエラーフラグを確認してクリアするHAL_UART_Abort関数を実行して受信処理を再開させます。
なお、HAL_UART_Abort関数は割込みを解除する関数ですが、各種エラーをクリアさせる機能も入っています。個々のエラーフラグだけをクリアするだけでは再開できないことが多々あるため、HAL_UART_Abort関数でレジスタをクリアさせる方法が確実です。



 if(huart->Instance==USART1){
  if (  __HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) ||
    __HAL_UART_GET_FLAG(&huart1, UART_FLAG_NE) ||
    __HAL_UART_GET_FLAG(&huart1, UART_FLAG_FE) ||
    __HAL_UART_GET_FLAG(&huart1, UART_FLAG_PE) ){
     HAL_UART_Abort(&huart1);
     HAL_UART_Receive_DMA(&huart1, (uint8_t*)&UART_Data, 1);//DMAの場合
     //HAL_UART_Receive_IT(&huart1, (uint8_t*)&UART_Data, 1);//割込みの場合
  }
 }


上記処理をmain.cのwhile内等に配置することでエラー処理することができます。


以前に通常の処理ではうまく受信できるがタイミングによって受信できなくなるといった不具合に悩まされました。原因がフレーミングエラーと分かりました。電源投入直後のノイズ等でエラーが発生してしまったようです。上記の処理を加えることでエラー時に処理を再開させ、安定して通信を継続できるようになりました。

※一部HALライブラリ依存があったため、修正しました
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2019年12月14日

STM32F3マイコンでの3線SPI通信


STM32F3マイコンの3線SPI通信で少し癖があったので少し紹介します。


CubeMX上ではHalf Duplex Masterとして設定します。

SPI.png



3線式ではHalf Duplexとなるため、HAL_SPI_Transmit関数とHAL_SPI_Receive関数を使用します。今回、読み込みコマンドをHAL_SPI_Transmit関数でデバイスに送信してから値をHAL_SPI_Receive関数で読み込みます。その際にHAL_SPI_Receive関数でタイムアウトエラーが頻発し、安定して読み込みできない現象が発生しました。



使用するバージョンにもよりますが、HALライブラリに少し癖があるようです。HAL_SPI_Transmit関数の後に続けてHAL_SPI_Receive関数を呼び出すとRXNEレジスタが変わらず、タイムアウト状態になってしまうようです。
※現時点ではSTM32Cube FW_F3 V1.11.0


3線式ではHalf DuplexではGPIOの入力、出力を随時切り替えて通信する必要があります。上記のタイムアウトを防止するため、強制的に入出力を切り替える関数を呼び出しすとタイムアウトエラーが発生せずに読み込めました。

具体的には下記の通りです。

SPI_1LINE_TX(&hspi1);//強制的に送信モード
HAL_SPI_Transmit(&hspi1,sdata,2,0x1000);
SPI_1LINE_RX(&hspi1);//強制的に受信モード
HAL_SPI_Receive(&hspi1,rdata,4,0x1000);



強制的な入出力を切り替えで読み込めることが確認できました。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2019年05月11日

STM32 Cube.AI 試食 その2 XOR

前回はSTM32 Cube.AIの紹介動画をベースに使い方を紹介しました。

前回は学習済モデルを使用したため、あまりAIやってる感がありませんでしたが、今回は実際の開発フローと同じように学習から推論まで行ってみました。

TensorflowとKeras、STM32Cube.AIを使って、XORの論理演算を学習させ、STM32マイコンで推論させてみました。
ターゲットマイコンとしてSTM32F747G-Discoveryを使用しました。

【準備物】

・STM32F747G-Discovery
・KerasとTensorflowインストール済みのPython3環境
・STMCubeMX5.1& Cube.AIパッケージv3.4
・SW4STM32やTrue Studioなどの開発環境


■PCでXOR論理演算学習
@XOR論理演算を学習させます。
 KerasとTensorflowインストール済みの環境でXOR.pyコードを実行します。
 「python XOR.py」
 学習させた結果、accが最終的に1になっていることを確認してください。
 0.75といったように1になっていない場合は何度か実行し直してください。

 また、最後に学習モデルを使って推論を実行しています。
 [0,0]、[0,1]、[1,0]、[1,1]をテストデータとしてそれぞれ与えており、
 0,1,1,0と表示されれば、XORの推論ができていることが分かります。


A学習モデルの保存
 コード実行後はコードと同じディレクトリにモデルがXOR.h5として保存されます。後でCubeMXで取り込んで使用します。

■CubeMXを使ったCube.AIプロジェクト作成
CubeMXにCube.AIのパッケージを追加する方法は前回の記事を参考にしてください。
@ターゲットボードの選択
 「Access to Board Selector」からSTM32F746G-Discoveryを選択します。


Aペリフェラル設定
 ボード選択後のペリフェラル設定はデフォルトモードyesを選択します。

 target-2.jpg



 「Computing」項目の「CRC」を有効化します。


 STM32F746DiscoはUSART1でPA9、PB7でSTLinkで接続されています。
 推論結果等をシリアルコンソールで表示するため、
 Connectivity→Usart1を有効化し、ボーレートを設定します。


 また、必要に応じてデバッグ用のSTLinkを有効化します。


CORTEX_M7をクリックし、IとDのCacheを有効化します。

 cubeAI-12.jpg

Bクロック設定
デフォルトは16MHzのため、最大の216MHzを入力します。
他の欄をクリックするとクロックウィザード確認がでるため、
OKボタンを押して他のクロックも合わせて自動設定します。


CCube.AIパッケージ追加
「Additional Softwares」をクリックして、X-Cube-AI/Application内の設定を有効化します。
 Coreライブラリにチェックを入れ有効化し、Applicationは「Application template」を選択して「OK」ボタンで画面を閉じます。



Dライブラリ設定
「Additional Software」に「X-CUBE.AI」が追加されます。
「X-CUBE.AI」をクリックして、「Artificial Intelligence Core」、「Artificial Intelligence Application」にチェックを入れて有効化します。


ECube.AI設定
「Additional Software」の「X-CUBE.AI」をクリックします。
「Add network」をクリックし、「XOR」モデルを追加します。
ライブラリに「Keras」、「Saved model」を選択し、先ほど学習させたModel「XOR.h5」を選択します。「Analyze」ボタンをクリックします。


 今回はXOR論理演算で簡単な処理のため、RAM40Byte、Flash:132Byteと圧縮なしでも非常に小さく実装できることが分かります。選択した学習済モデルで正常に推論できるか確認します。

 今回はXOR論理演算のため、ランダムを入力するよりも論理演算として想定されるデータを入れて確かめた方がよいため、「Validation from」からカスタムデータai_inputs.csv を選択します。
 「Validation on desktop」をクリックし、「Validation status」がSuccessとなれば正常に処理が終了したことを示します。

 下記のフォルダにアクセスして、ai_valid_outputs_model.csv、ai_valid_outputs_c_model.csvを確認してください。正常に処理が終了すると処理結果がcsvファイルとして出力されているはずです。

C:\Users\(ユーザー名)\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-AI\(バージョン)\Utilities\windows

[1,1]の入力に対して約0.06
[1,0]の入力に対して約0.94
[0,1]の入力に対して約0.95
[1,0]の入力に対して約0.04



値を丸めると
[1,1]の入力に対して0
[1,0]の入力に対して1
[0,1]の入力に対して1
[1,0]の入力に対して0

ということでXOR論理演算の推論ができていることが分かります。

Fプロジェクト設定とコード生成
 HEAPサイズを0x200から0x2000に設定して、
他の設定は使用する開発環境等に合わせて設定し、
「Generate Code」をクリックします。

cubeAI-15.jpg

 なお、学習済データ等はMiddlewares\ST\AI\AI\data、Middlewares\ST\AI\AI\src
 Cube.AIライブラリ等はMiddlewares\ST\AI\AI\include、Middlewares\ST\AI\AI\lib
 ユーザーが呼び出す関数群はsrc\app_x-cube-ai.cに入っています。

 また、学習済モデルXOR.h5から推論の入力数、出力数は自動で設定されます。

Gprintf設定
CubeMXで生成したプロジェクトを開発環境にプロジェクトを追加します。
floatの結果を出力するため、プロジェクトのプロパティからいつも通り下記を追加して設定します。
-u_printf_float


ビルドします。

Hコード追記
main.c内に推論を行うための関数、コードを記述します。
今回はmain.cのみ手を加えました。
他のコードは自動生成されたコードです。
コードはこちらを参照してください。

code.jpg

aiTest()を呼び出して推論機能の初期化と実行をしています。
XORの学習モデルに合わせて入力に2次元の配列、出力に1次元の配列を与えています。
なお、自動で生成されるMX_X_CUBE_AI_Process()は空です。

Iマイコンへ書き込みと推論実行

xor-result.jpg

シリアルUartで出力させ、teratermでデバッグ表示させてみました。
学習させたXOR.h5モデルを使用してSTM32マイコン上で入力に対してXORの推論ができていることが分かります。


XOR論理演算を学習させてSTM32マイコンで推論させてみました。
XORモデルは非常に小さいため、ターゲットマイコンがSTM32F3シリーズでも実現できそうです。

今回はマイコン側のXORの入力は固定値ですが、スイッチ入力やセンサ、ADCなど変化する値にするとAIがより機能的だと実感できると思います。
例えば、入力を1x2でなく、もう少し大きな配列にしてアナログ値やセンサ入力等を学習させて、それに応じて音や光といった結果を返すことも実現可能です。

今回作成したコードとプロジェクトはこちらにアップしてあります。
今回はSTM32F7-Discoveryのディスプレイを全く使用していないため、今後はタッチパネルを活用して推論させたりしてみたいと思いました。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2019年01月12日

STM32Cube.AI 試食

19年の年始早々にSTM32Cube.AIがリリースされました。
STM32Cube.AIはPC等で学習させて
STM32マイコンで推論させることができます。

まさにAIのエッジコンピューティングを実現するツールです。

推論の動作テストをする手順について紹介します。

今回作成したコードとプロジェクトはこちらにアップしてあります。

まず、ターゲットはF3,F4,L4,L5,F7,H7が対応しています。
ただ、ROM、RAMを多く使うため、実際はF7、H7が現実的だと思います。

Caffe、Keras、Lasagneの学習済ファイルを使用して推論させますが、
今回は動画内で使用されているファイルを使用します。

【準備物】

・F3,F4,L4,L5,F7,H7ターゲットデバイス
・学習済みモデル 生ファイルはこちらから。
 https://github.com/Shahnawax/HAR-CNN-Keras
・STMCubeMX5.0以降
・SW4STM32やTrue Studioなどの開発環境


@Cube.AIのパッケージを追加する
 STMCubeMX5.0以降はHALライブラリ以外の関連ライブラリも
簡単にダウンロードできるようになっています。
CubeMXを開いて、
「Embedded Software Package Manager」の
「STMicroelectronics」タブから
Cube.AIにチェックを入れてパッケージをインストールします。

CubeAI.jpg

回線が細い場合は中途半端な状態でパッケージが入り、
学習済モデル読み込み時に「Invalid network」という
エラーが出る場合があります。
その場合は中途半端なパーケージを削除後、
ZIPを解凍して「Embedded Software Package Manager」の
「From Local」からインストールを選択してください。

削除はC:\Users\(USER_NAME)\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-AI
のフォルダを全削除します。


ASTM32マイコンの選定
学習済モデルの推論に必要なROMとRAMを確認し、
STM32マイコンの選定します。
「MCU Selector」の「Artificial Intelligence」に
Enableのチェックを入れて
Model「Keras」
Type「Saved Model」
Model「(準備物のgithubファイル)」
Compression「None」
でAnalyzeをクリックします。


cubeAI-2.jpg


フラッシュ2.82MByteとなり、
フラッシュメモリが足りず、
このままでは推論できません。

Compression「4」
でAnalyzeをクリックします。

cubeAI-3.jpg

フラッシュ775kByteとなり、
STM32マイコンで推論できる現実的な値となります。
今回は手持ちにあったSTM32F746-Discoを使用するため、
STM32F746NGHを選択しました。


Bターゲットボード設定
STM32F746NGH選択後、「Additional Softwares」をクリックして、
X-Cube-AI/Application内の設定を有効化します。
Coreライブラリにチェックを入れ有効化し、
Applicationは「Validation」を選択して「OK」ボタンで画面を閉じます。

今回はライブラリの検証のため、
「Validation」を選択しますが、
速度検証の場合は「System Performance」、
推論のユーザーコードを実装する場合は
「Application template」を選択します。

cubeAI-4.jpg

cubeAI-5.jpg

Cライブラリ設定
「Additional Software」に「X-CUBE.AI」が追加されます。
「X-CUBE.AI」をクリックして、
「Artificial Intelligence Core」
「Artificial Intelligence Application」にチェックを入れて有効化します。

最初、「Additional Software」に「X-CUBE.AI」が表示されず、
選択できない不具合があり、CubeMX自体とCube.AIパッケージを再度、
再インストールし直すと表示されるようになりました。

cubeAI-6.jpg

DCube.AI設定
「Additional Software」の「X-CUBE.AI」をクリックします。
networkタブを選択します。表示がない場合は+で追加します。
ライブラリに「Keras」、「Saved model」を選択し、
Model「(準備物のgithubファイル)」を選択します。
Compression「4」を選択して、
「Analyze」ボタンをクリックします。

cubeAI-7.jpg


Eファイルの検証
「Validate on desktop」をクリックして、
学習済ファイルの検証をします。
Validation StatusにSuccessが表示されたら検証完了です。

cubeAI-8.jpg

Fその他設定
「Computing」項目の「CRC」を有効化します。

cubeAI-9.jpg

STM32F746DiscoはUSART1でPA9、PB7でSTLinkで接続されています。
USART1を有効化します。

cubeAI-10.jpg

また、必要に応じてデバッグ用のSTLinkを有効化します。

cubeAI-11.jpg


CORTEX_M7をクリックし、IとDのCacheを有効化します。

cubeAI-12.jpg


Gクロック設定
デフォルトは16MHzのため、最大の216MHzを入力します。
他の欄をクリックするとクロックウィザード確認がでるため、
OKボタンを押して他のクロックも合わせて自動設定します。

cubeAI-13.jpg

H出力設定
推論した結果等の情報をUsart1に出力します。
再度、「Additional Software」の「X-CUBE.AI」をクリックし、
「Platform Setting」から先ほど選択したUsart1を選択します。


cubeAI-14.jpg

Iコード自動生成
 HEAPサイズを0x200から0x2000に設定して、
他の設定は使用する開発環境等に合わせて設定し、
「Generate Code」をクリックします。

cubeAI-15.jpg

J開発環境にプロジェクトを追加し、ビルドします。
SW4STM32ではCubeMXで生成したプロジェクト設定に
バグがあるようです。
そのままではパスが通らず、ビルドできませんでした。

C/C++General内のPath and Symbols、IncludeタブのGNU Cで
デフォルトではDebug/Middlewares/***となっているため、
Debug/を削除してApplyボタンを押して適用します。

cubeAI-16.jpg

同様にLibrary Pathタブでも
Debug/を削除してApplyボタンを押して適用します。

cubeAI-17.jpg

念のため、C/C++ Build内のSettingのMCU GCC Linkerの
Librariesで先ほどの設定が反映されているか確認します。

cubeAI-18.jpg

Kビルド&書き込み
STLink等でSTM32F7へ書き込みを行います。

Lデバイス上での検証

先ほど生成したプロジェクトフォルダ内の
*.iocファイルをダブルクリックして、CubeMXを開きます。
「Additional Software」の「X-CUBE.AI」をクリックし、
networkタブからValidate Targetをクリックします。

cubeAI-19.jpg


デバイスのCOMポート確認画面が表示されるため、
デバイスマネージャ等で対象のデバイスCOM番号を選択します。

cubeAI-20.jpg

CubeMXのWindowでOutputにチェックを入れて結果を表示させます。
cubeAI-21.jpg


CubeMXの画面下に出力画面に結果が表示されました。

cubeAI-22.jpg

ValidationはOKで、入力した値(今回はrandom数)に対して
推論した値とのerror値を確認できます。

入力する値はCustomDataを選択してcsvファイルとして入力して、
検証することもできます。

cubeAI-23.jpg


ということでCube.AIを使ってデバイス上で推論することができました。
Cube.AIはAIライブラリとしては
非常に使いやすいソフトウェアとなっていますが、
まだインストールに少し難があります。
インストールミスで選択画面が表示されないといったエラーがありました。
何度かパッケージの再インストールで動作できるところまでできました。
インストールミスは今後のアップデートで改善すると思います。

今後は実際にセンサから人の姿勢の推定といったAIを活用してみたいと思います。
今回作成したコードとプロジェクトはこちらにアップしてあります。


posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2018年11月10日

STM32F373 SDADCマルチチャンネル読み込み

今回はSTM32F373マイコンに内蔵されている
デルタシグマADC(SDADC)のMulti Channelでの
読み込みする方法を紹介します。

SDADCは一部のSTM32マイコンのみ内蔵のため、
サンプルコードや情報がなかなかありません。

試行錯誤してSDADC+DMA+2channelという条件で動作したため、
方法を覚書として紹介します。



今回はSDADC1の2チャンネルを順次変換します。


CubeMX上の設定は下記の通りです。
ポイントは
・Injected Conversions有効化
・DMA転送
です。


・Injected Conversions :Enable
・Number of Channels To be converted :2
・Continuous Mode :Enable
・Channel Configuration1,2: Channel 4,5


sdadc-conf.jpg


・Mode: Circular
・Increment Address: Memory Checked


sdadc-dma.jpg



main.cでは下記のコードを追記します。

//グローバル変数
uint16_t g_SDADCBuffer1[2];

//while文の前に初期化
HAL_SDADC_InjectedStart_DMA(&hsdadc1 , (uint32_t*)g_SDADCBuffer1,2);

//while文
printf(" %d,\t %d, \n\r",g_SDADCBuffer1[0],g_SDADCBuffer1[1]);
HAL_Delay(100);


g_SDADCBuffer1[0]にChennel 4
g_SDADCBuffer1[1]にChennel 5
がDMA転送で格納されます。


実際に実行した結果は下記の通りです。
順次SDADCの変換結果が格納されていることが確認できます。

sdadc-res.jpg

DMA転送でContinuous Mode :EnableかつCircularモードの場合は
void HAL_SDADC_ConvCpltCallback(SDADC_HandleTypeDef* hsdadc)の
関数内でDMA割込みの設定し直す必要はないようです。


HAL_SDADC_PollForConversionを使用すると変換待ちの時間を要するため、
DMA転送できると変換待ちがなく、色々応用範囲が広がります。

posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする