2019年5月12日日曜日

CubeIDE+STM32F413CHでprintfを使う話



 PYNQかZYBO Z7-20が欲しい中の人です(誰かくれないかなぁ)。前回からの続きで、printfでfloatを表示させる話です。


 マイコンのデバッグでは、UARTを出力先に指定したprintfを実行させることがあります(なんか変な感じするけど)。STM32の場合syscalls.cの__io_putchar()を書き換えてUARTに吐き出したり(weak定義に関してはこちらから)、_write()関数の中身を書き換えることで実装するのが楽なようです(こことかここを参照)。

1. とりあえずprintfをUARTで使えるようにする

*UARTが使えている前提で話を進めていきます。HAL_UART_Transmitとかが使えていれば大丈夫。

 CubeIDEでも同様にしてprintfの出力先をUARTに変更できます。/(プロジェクト名)/Srcの中にsyscalls.cがあるはずなので、それを開き_write()のところまで行きます。で、その中身をこんな感じで書き換えます。
__attribute__((weak)) int _write(int file, char *ptr, int len) {
 int DataIdx;

 for (DataIdx = 0; DataIdx < len; DataIdx++) {
//  __io_putchar(*ptr++);
  HAL_UART_Transmit(&huart1, ptr++, 1, 1);
 }
 return len;
}

 このままコンパイルすると"huart1ってなんだよ"って怒られるので、ヘッダと変数の宣言部分に

/* Includes */
#include 
#include 
#include 
#include 
#include 
#include 
...
と書き換えておきます。これでprintf()を実行するとTerminalとかになんか出てくるはずです(%fに関しては後述)。

2. %fを出力する

さて、ここまででprintfは%fフォーマットを除いて動くようになったはずです。で、%fはこのままでは動きません(TrueStudioやSW4STM32同様)。原因はリンカの設定なのでここを修正していきます。

*ここからの話はSTM32Cube FW_F4 V1.24.1での話です(2019/05/10時点で最新)。バージョンの違いに注意してください。

 とりあえず、リンカオプションの設定から。



Project->Propertiesを選択し、C++/C Buildを選択します。


 次にSettings->MCU GCC Linkerを選択し、Other flagsに"-u _printf_float"を追加します。
 ここまで終われば動きそうな気がしますが、うまくいかない(なぜいきなりTrueStudioやSW4STM32をNRNDにしたし)。この問題に気が付いたのがGWの中ごろで、そこから1週間ほどとかしました。(´・ω・`)
 この原因についてはまだわからないこともありますが、ここを参考にすることで%fを有効化できました。手順としては、STM32F413CHUX_FLASH.ldの_estackの値を0x2004FFFFから0x20050000に書き換えるだけです。この値に関しては、データシートの21ページに書いてある

"CPU can access SRAM1 memory via S-bus, when SRAM1 is mapped at the address range: 0x2000 0000 to 0x2003 FFFF. CPU can access SRAM2 memory via S-bus, when SRAM2 is mapped at the address range: 0x2004 0000 to 0x2004 FFFF."

を見ながら決めました。_estackにはRAM領域の終端アドレスを指定するのですが、どうやら奇数だと動かず、偶数でないと動かないらしいです(この辺検証とかもできていないので怪しいですが、0x20050000や0x2004e200、0x20040000の場合は動き、0x2003FFFF、0x2004FFFFでは動いていないことは説明できそう)。

2019/05/13追記
@osaboh さんからの指摘で、Application Binary Interface(ABI)の呼び出し規約の中に

5.2.1.1 Universal stack constraints At all times the following basic constraints must hold:
(中略)
 SP mod 4 = 0. The stack must at all times be aligned to a word boundary.
(中略)
5.2.1.2 Stack constraints at a public interface The stack must also conform to the following constraint at a public interface:
 SP mod 8 = 0. The stack must be double-word aligned.

とあるので、アドレスは4 or 8 Byteで割り切れる必要があるそうです。

参考:Procedure Call Standard for the ARM Architecture

謝辞

@osaboh さん@sora_siro1 さん、@Sakurai_Tatuyosさんには%fが表示できない件に関して、動作検証や改善手順の提案などで有益な情報をいただきました。本当にありがとうございます。


©2018 shts All Right Reserved.

0 件のコメント:

コメントを投稿