今まではAVRマイコンのお話をしてきましたが今回はSTM32マイコンを使った応用的なお話をします。(STM32マイコンの基本的な所は今回は書かないので他の文献を…)

 今回紹介する内容はSTM32に搭載されているDMA(CPUを介さずにペリフェラルからメモリに書き込むorその逆)を使ってUART(シリアル通信)の受信データをArduinoSerialライブラリのように使えるように実装してみました。

 

DMAとは

 DMAは略さずに言うと「Direct Memory Access」です。すなわちペリフェラル(UARTSPIなどの周辺回路,周辺機器)CPUを介さずに直接メモリにアクセスすることができます。これによりCPUの負荷に関係なく確実にデータの受け渡しが可能になります。図解するとこんな感じです。
0-1

0-2

0-3

STM32における各種設定

 STM32マイコンでは各種設定はCubeMXを使って行います。その設定内容・方法を記載します。
 画面の全体画像はこんな感じです。
キャプチャ

まずは右側のペリフェラル選択画面からUSART*またはUART*(*は数字)からDMAで受信したいUARTの番号を選択します。今回はUSART2(USARTUARTの機能拡張なのでUARTを使う場合はUSART*を選んでもOK)
1

続いてペリフェラル選択画面の右上の下図に示すようなMode選択画面から←で示したAsynchronous(非同期通信, UART)を選択します。MIDIArduinoなどの通信は基本的には非同期通信となります。もし、同期通信(USARTの機能)を使う場合は用途に応じて選んでください。
2

Asynchronous」を選ぶとその下の画面には通信速度の設定などのconfigurationのメニューが表示されます。Baud Rate(通信速度)は通信相手に合わせて設定します。MIDIの場合は31250Bits/sです。他の設定も相手側に合わせます。MIDIの場合は通信速度以外はデフォルトでOKです。設定が完了したら下図↓で示したDMA Settings」を開きます。
3

 続いてDMAの設定に入ります。まずは下図の↑1の「Add」をクリックしDMAの設定を追加します。続いて出てきたDMAの設定の「DMA Request」の「Select」から←2で示した「USART*_RX(今回の場合*2)を選びUARTの受信データをDMAで受け取る設定にします。
4

 続いて「DMA Request Settings」のModeから下図←3の「Circular」を選択します。これによりDMAで受け取ったデータのバッファーがループ形状になります。(厳密にはバッファーの最後までデータが受信されると最初に戻る 例:Bufferサイズが1024の場合1023まで受信したたら次は0) DMA優先度を設定する「Priority」は他にDMAを使う場合は優先度が高いものからVery High, High, Medium, Lowにします。
 5

これにてCubeMxでのUART周りの設定は終わりです。Generate Codeでコードを出力しておきます。

 

 続いて生成されたコードを開きソースコードを書き始めます。ここで今回のシステムの構造とアルゴリズムを示します。

 今回のシステムはArduinoSerialライブラリのような使い方、つまり未読データ数の確認と未読データの取得の関数を実装します。そのため、実装する関数は2つで何かしらに未読データ数を確認する方法を編み出さなければなりません。

 未読データ数を取り出す方法として、DMAで受信したデータ位置と既に読み取り済みのデータ位置を比較します。読み取り済みのデータ数はプログラムで読み取るごとにインクリメントすれば導けます。受信済みのデータ位置には筆者がいろいろと試した結果以下の方法で読みとりが出来ました。

UARTのハンドラ(huart*(今回は*2))内のUART RxDMAのハンドラ(hdmarx)内のInstance, NDTRにてバッファーの残容量の確認ができます。具体的には以下のようなコードでremain_buf変数に残りのバッファー量を読み取れます。


 remain_buf = huart2.hdmarx->Instance->NDTR

残りのデータ量がわかればバッファーサイズからこの値を引けば現在受信済みデータの先頭位置がわかります。受信済みデータの先頭から読み取り済みデータ位置を引くと未読データ数が判明します。(バッファー端部の例外処理は必要)

 具体的なコードは以下の通りです。

未読データ数確認
6

データ読み取り
7

以上で関数の実装が出来ました。これらのコードはcubeMxの吐き出した「main.c」の/* USER CODE BEGIN 0 *//* USER CODE END 0 */の間あるいは、別のファイルに入れなければなりません場所を間違えるとCubeMxの設定変更時にコードが消えます

また、別途バッファーの宣言とバッファーのサイズ(データ数)DATANUM」の宣言が必要です。

 

実際にDMAでデータを受信するためにはDMAの動作を開始する指示をマイコンに与えなければなりません

その処理は以下のような関数(HALドライバの関数)を実行します。

HAL_UART_Receive_DMA(&huart2,serialData,DATANUM);

この関数はループ処理が始める前すなわちwhile(1){}の前に入れる必要があります。具体的な位置としてはCubeMxが吐き出した「main.cUSER CODE BEGIN 2USER CODE END 2の間で良いでしょう。場所を間違えるとCubeMxの設定を変更すると消えてしまいます

 

以上がSTM32UARTDMAで受信してArduinoSerialライブラリのように読み取る方法の紹介です。なお、今回のコードはhttps://github.com/HanDenMotor/readSerial_STM32DMAにアップしてるのそこからコピペするなりなんなりで使ってください。

次回は今回のUART受信をさらに応用してMIDIの受信プログラムの紹介をします。