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では上記二つの機能を体験可能です。同ボードに
搭載されたTFTLCDの解像度は工作物ではメジャーな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