STM32F4シリーズを使ってみる11 - STM32F429I-Discovery発動編 -

ぇー…前回のSTM32の記事の最後で新しいコードスニペットと称するどことなく卑猥な名称
のふれこみで発表されたSTM32CubeMXとそのF4シリーズのサポートを行うCubeF4という
新たなファームウエアライブラリに触れましたが…
私も移植を目指して頑張ってたのですが…

私の従来のいつものに当てはめようと思うと改造個所が多岐に及び、さらにコードサイズ
とオーバーヘッドも増加してしまったので移植は見送りにして新規に作るプロジェクトから
CubeF4を使用する方針に逃げ変えました。

STM32CubeF4には抽象化がさら進められ、インスタンスという概念が追加されています。
これがかなりの曲者で従来のF4シリーズで言うDSP_StdPeriph_Driverライブラリに相当
する"STM32F4xx_HAL_Driver"はすべてこれの使用を前提としていてさらにサイズが
大きい構造体と併用しなければならないのが分かりました。これSRAMやFLASHのリソース
が膨大にあるF4シリーズだからよいものをF1/F0にも当てはめるのはかなりヤバいと思います。
私も過度の抽象化は良いとは思っていません。移植性を重視しすぎてせっかくのマイコン
の性能を引き出せないのであれば本末転倒です。

あんまし考えたくないのですが無理くそ全CPUの格差を埋めるべく抽象化を目指し、
そして失敗して結局ライブラリのバージョンが多岐に分かれてしまったLPCOpenと同じ
道を歩んでいる感じがします。
おそらくCube系のライブラリに遷移していくあたって大混乱が起こると思いますね。
無理にCubeへの遷移を煽る中途半端に発言力が持ってる人が湧くのは容易に予測
されます。学生さんなんかは自分のボスの"せんせい"にまたCubeライブラリの使い方を
wikiにまとめさせられると思います。そういうこと言ってくる人らには"こんなことしてる
こと自体が時間の無駄。したけりゃ自分でやれ!"とはっきりNOを突き付けるべきです
とはいえ私のぶろぐを見られている方は私よりも技術力がはるかに上の方ばっかな
のでそもそもこんなとこ見てない私が注意しなくても各自しっかり対処出来することが
できるでしょうからぐだぐだ言いましたがやっぱし私の杞憂かなと思います。




さて、前置きが長くなってしまいましたが以前から触れてきたSTM32F429I-Discoveryの
TFT-LCDをSPIではなくRGBインターフェースで動かすことに成功しました。
前回は低速なSPIインターフェースでお茶を濁していました。しかしSTM32F429シリーズ
にTFT-LCDに特化したグラフィックコントローラ(LTDC)とグラフィックに特化したDMAである
(Chrom-ART Accelerator)が搭載されているのでこれらの機能を使っていつものの再現
を目指しようやく完成したわけです。

●STM32F429Iにあるグラフィック機能
私が説明するよりもこちらのPDFを見たら一目瞭然です。
LTDCに関してはフレームバッファとして確保したメモリアドレスにRGBのデータを書き込む
だけで色の表示が可能です。また、STM32F429系ではSDRAMがサポートされているので
容量を喰う画像データでも余裕で取り扱うことができます。

Chrom-ART Acceleratorに関しては別名DMA2Dと称されていて文字通り2Dグラフィック
特化型DMAです。しかもデータ形式を変換してメモリ間転送したり単一データを指定して
転送する(レクタングルフィルに効果を発揮)ことができます。

STM32F429I-Discoveryでは上記二つの機能を体験可能です。同ボードに搭載されたTFT
LCDの解像度は工作物ではメジャーな240x320となっています。STM32のLTDC単体では
レクタングルを指定して書き込み時にアドレスを自動インクリメントしながら連続で書き
込める機能はない(DMA2Dで解説します)ためアドレス計算は必須です。
具体的にいうとたとえば座標x,yにドットを打ちたい場合は基本的には先ず以下のように
座標を指定します(RGB=565,1ドットあたり2Byteのデータを想定)。


lcd_c_buf_ptr = lcd_f_buf_ptr + 2*(x + (MAX_X*y));

lcd_c_buf_ptr :ドットデータを実際に書き込むメモリのアドレス
lcd_f_buf_ptr :フレームバッファとして確保したメモリ領域のベースアドレス
MAX_X :TFT-LCDのX軸の最大ドット数(F429I-Discoveryでは240と定義)


次にRGBデータcolourを書き込みます。

*(volatile uint16_t*)(lcd_c_buf_ptr) = colour;
lcd_c_buf_ptr +=2;

より具体的な実装はいつものの./lib/display/mcu_depend/src/lcdc_if_basis.cをご覧
下さい。FONTX2等の描画ルーチンは完全にドット単位で行っていますがMCUバスと外部
LCDコントローラとの通信オーバーヘッドがなくなるのでドット単位でも十分に早くなり
ます。また、LTDCは2枚のレイヤーを使用可能でさらにアルファブレンディングの透過
効果も使用可能です。現状では単一のレイヤとしてしか使用していませんがこちらも
学習を重ねてモノにしていきます!


次にもう一つの目玉機能Chrom-ART Accelerator(以下DMA2D)です。私のいつものでは
FillRect系のレクタングル指定塗りつぶしやレクタングル指定の動画データ転送に使用
しています。

基本的には通常のDMAと同じですがグラフィック特化なのでRGBデータを格納するレジ
スタもあります。単一色描画では以下のように設定します。
※RGB565,黒色,全画面(240x320)塗りつぶしの場合

/* configure DMA2D */
DMA2D_DeInit();
DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M;
DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565;
DMA2D_InitStruct.DMA2D_OutputGreen = (0x07E0 & COL_BLACK) >> 5;
DMA2D_InitStruct.DMA2D_OutputBlue = 0x001F & COL_BLACK;
DMA2D_InitStruct.DMA2D_OutputRed = (0xF800 & COL_BLACK) >> 11;
DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F;
DMA2D_InitStruct.DMA2D_OutputMemoryAdd = (uint32_t)lcd_f_buf_ptr;
DMA2D_InitStruct.DMA2D_OutputOffset = (MAX_X - 1));
DMA2D_InitStruct.DMA2D_NumberOfLine = MAX_X+1;
DMA2D_InitStruct.DMA2D_PixelPerLine = MAX_Y+1;
DMA2D_Init(&DMA2D_InitStruct);
/* Start Transfer */
DMA2D_StartTransfer();
/* Wait for CTC Flag activation */
while(DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET){}


lcd_f_buf_ptr :フレームバッファとして確保したメモリ領域のベースアドレス
MAX_X :TFT-LCDのX軸の最大ドット数(F429I-Discoveryでは240と定義
MAX_Y :TFT-LCDのY軸の最大ドット数(F429I-Discoveryでは320と定義


フォアグラウンドのレイヤーへ画像データを転送したい場合は以下のように。

/* configure DMA2D */
DMA2D_DeInit();
DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M;
DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565;
DMA2D_InitStruct.DMA2D_OutputGreen = 0; /* M2M系転送の場合は使わない */
DMA2D_InitStruct.DMA2D_OutputBlue = 0; /* M2M系転送の場合は使わない */
DMA2D_InitStruct.DMA2D_OutputRed = 0; /* M2M系転送の場合は使わない */
DMA2D_InitStruct.DMA2D_OutputMemoryAdd = (uint32_t)lcd_f_buf_ptr;
DMA2D_InitStruct.DMA2D_OutputOffset = 1;
DMA2D_InitStruct.DMA2D_NumberOfLine = MAX_X+1;
DMA2D_InitStruct.DMA2D_PixelPerLine = MAX_Y+1;
DMA2D_Init(&DMA2D_InitStruct);

/* Configure default values for foreground */
DMA2D_FG_StructInit(&DMA2D_FG_InitStruct);
DMA2D_FG_InitStruct.DMA2D_FGMA = (uint32_t)p;
DMA2D_FGConfig(&DMA2D_FG_InitStruct);

/* Start Transfer */
DMA2D_StartTransfer();
/* Wait for CTC Flag activation */
while(DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET){}


lcd_f_buf_ptr :フレームバッファとして確保したメモリ領域のベースアドレス
MAX_X :TFT-LCDのX軸の最大ドット数(F429I-Discoveryでは240と定義
MAX_Y :TFT-LCDのY軸の最大ドット数(F429I-Discoveryでは320と定義
p :転送したい画像データのポインタ

と、従来のDMAと同じ感覚で使用が可能です。もっと汎用なDMA2Dの使用法は同じく
lcdc_if_basis.cをご参照ください。DMA2DはLTDCと組み合わせると強力なレイヤー機能が
使えるようになるでしょうね。私のサンプルでは全く使いこなせてませんがー!


という訳でそれを踏まえたいつものをビルドして書き込み、動作させたものが上の画像です。
静止画では違いが分からないと思いますが以前の8-bitSPIのみで動かしてたものとは
描画速度がダンチですよ♥


惜しむらくはSTM32F429I-Discoveryに使用されているTFT-LCDが発色にちょっと劣る点
です。2014年現在では超高視野角液晶はすでにホビイストでも容易に入手可能なので
それに慣れた私にとってはあと一歩がほしいところと感じました(多分最新の奴搭載し
たら値段跳ね上がるでしょうけど)。




いつものFatFs+TFTLCD表示サンプルの細かい整備など

LTDC+DMA2Dに対応したついでに長らく放置していた全部載せSTM32F427IIT6基板
プログラムも見直して安定化を図りました。いまさら気付いたのですが、新しいSTM32F4に
存在するFMC(フレキシブル・メモリコントローラ)ではFSMCの設定とは微妙に違っておりました…。

なんとFSMCでは存在しなかった変数が構造体に追加されていてこれを設定してなかった
せいで動作が不安定になっておりましたorzいまさら気付きましたがまだFMC世代の新しい
STM32F4を使用されている方が少ないのか指摘されず助かりましたうふふふふふふふf

また、この全部載せ基板はI2Cのコーデックも乗っていて音質は非常に悪いですがmp3や
waveファイルの再生も可能です。私のいつものプログラムではSDIO経由でSDカードのmp3
ファイルなどを読みだして再生します…がしかしビットレートが高いmp3の再生ではSDIOの
データ読みだしにDMAの転送を行うとI2Sの方のサーキュラモードDMA転送と干渉しまくり
あっという間にオーバーフローになってエラーをはいて止まります。

という訳でこれを避けるためにSTM32F4でもFIFOのポーリングでの読み書きに本格的に
対応させることにしました。STマイクロ配布のSTM32F4のデモではポーリングの読み
書きはRead/WriteBlockしか対応しておらず非常に低速度なのですがマルチブロック転送
でもポーリングで読み書きできるように改良しております。ポーリングでもDMAとほぼ
同等の転送速度が得られたので速度に関しては問題なしです。


…という訳でDMAあえてを使用せずポーリングによる読み出しだけで無事ビットレートの
高いmp3ファイルの再生も可能となりましためでたしめでたし!
(helixのフォルダにあるassembly.hのマクロが前回の機能追加で間違っててそもそもmp3
 ファイルが全く再生できなくなっていたのは見逃していただきたい)


●もいっこおまけ
私のいつものでは一部のmp3ファイルを再生したときにアーティスト/曲名情報表示の際に
ゴミデータが表示されてしまっておりましたが、こちらも文字列ストア用のバッファ(私の
プログラムではCCM領域にあたる)を一旦ゼロクリアしてから表示を行うようにしてバグを
潰しております。

以前は変なゴミデータが表示されていましたが…、

修正後はこんな感じにすっきりです★

Go to top of page