« 2010年12月 | トップページ | 2011年2月 »

2011年1月の2件の記事

2011年1月20日 (木)

フォトフレーム:SPIのDMA転送にこだわる

 雑誌付録のFPGA Lattice XP2基板と、これも雑誌付録のARMプロセッサーSTM32基板(CQ-STARM、但しCPUをSTM32F103VE6に換装)を組み合わせたデジタルフォトフレームプロジェクトは、JPEGファイルの画像描画に成功して、いよいよ最終ゴールである液晶モニターにFPGA基板やCPU基板を実装する段階に入った(第9ステップ、2010/3/11記事参照)。

 しかし、JPEGライブラリでデコードされたビットマップデータをSPIでFPGAに送る際のDMA(Direct Memory Access)がどうにも言うことを聞かない。CPUでSPIを動かすのと余り性能的に変らないようなのでこだわることはないのだが意地になっている。

P1203618

STM32のDMA転送がうまくいかない(1/12/2011)
 フォトフレームの現状は、JPEGの画像は何とか出たが、画は逆さまだし、DMAを使ってSPIを動かすと同期のずれた縞模様しか出力しない。しかも、おかしなことにDMAを使ったほうが何故か時間がかかる(2.2秒以上)。

 DMAを使わずにSPIをCPUを使って素直に書き出しても、BMPファイル(2.2秒)より早く(1.7秒)、画像が出せたので、もうこのままでも良いのだが、プロセッサーが本来持っている折角の機能を使わずにおくのは、ソフトウエア開発の元プロとしては寝覚めが悪い。しかも一旦やりだしたことを途中で投げ出すのは、負けず嫌い、へそ曲がりの所長としては断じて避けたいところである。

 BMPファイルよりJPEGファイルの方が早いのは、ファイルサイズが1/4以下になっているのでデコードのCPU処理が増えても全体としては早くなったからである。これにSPIをDMA転送にすれば、SPIを動かしながら、CPUはJPEGデコードに専念することができるので、今よりさらに早く転送が出来るはずだ。これを活用しない手はない。なんとしてもDMAを動かしておきたい。

 しかし、これが言うことを聞かないのである。JPEGがデコードしたビットマップのデータをDMAを使わずそのままSPIで出す画像は、きれいに出力されるのに、一行分のバッファーにそれを移してDMAを通してSPIで送ったものは同期がずれたような縞模様の画像で、全く画にならない。DMAがおかしいことは明白である。

 マニュアルをみるとバッファーサイズはバイト数ではなくデータカウント数で、16ビットフレームだから半分にしないといけないはずだが、そうすると画面にデータが送れていないところが残る。元へ戻すと全部描かれるが、どちらも化け化けの映像である。

 それにしても謎である。サイズを半分にしたら画面の半分のところで止まる筈なのだが、4/5のあたりで止まっている。例によって、16ビット画像の位置を逆にしたら(リトルエンディアン)少し原画らしいものが見えてきたが4つぐらいが重複した縞だらけの画像になる。良くわからない。

 DMAはCPUを経ない転送なので外から様子を調べることが難しい。DMAアクセスと実際のバッファーへの移動がかぶらないように、DMA転送の終了フラグを見るようにしているが、タイマーで測っても数msしか待っておらず、どうもおかしい。1ラインごとにDMAを動かしているので計算上では480ラインでは数百ms待たないとおかしい。

 それに、サンプルのソースコードはDMA転送の度に、DMAのDeinit(全ての設定値のクリア)から延々と初期化をしている。これがDMAが遅い原因なのか(CPU内の処理なのでそんなわけはないのだが)と、初期化と開始の関数を、わけて動かしてみるが、ENABLEコマンドだけではDMAは全く動かない。やっぱりDeinitから始まるプロセスでないとDMAは動かない。

やっとのことでDMAが動いた(1/15/2011)
 3日間悩んでいた。やはりもうすこし勉強しないと駄目なようだ。プロセッサーのリファレンスマニュアル(RM0008)をダウンロードしてDMAのところを本格的に調べ始めた。

 すると、マニュアルの10.3.3 DMA channels(ページ201)の最後の方で、「DMA転送を終えたら、次の転送のためにはチャネルを必ずdisableにしておくこと」(In order to reload a new number of data items to be transferred into the DMA_CNDTRx register, the DMA channel must be disabled.)というのを発見した。いやあ、こんなところに大事なことが隠れていた。

 そうか。これが転送の度に初期化ルーチンを動かしている理由なのか。DMA開始関数に、ENABLEの前に、DMA_cmd(DMA1,DISABLE)を入れてみると、やっとDMAが動き出した。しかし画像が改善されるわけではない。

 画像の化ける原因がつかめない。ロジックアナライザーでデータをひとつひとつ調べるしかないのか。考えられるのは1行の転送が終わらないうちに次の転送が始まってデータをこわしている可能性である。転送終了フラグは初期化のときにクリアされているはずで、次のDMA転送に行くのにはこのフラグが立つのを待っているが、どうもこのあたりが動いていないような気がしてきた。転送終了を待つ時間が少ないというのも気になる(ただし0ではない)。

 DMA開始関数のところに、転送完了フラグをクリアする関数、DMA_ClearFlag(DMA1_FLAG_TC5)を入れてテストしてみる。うーむ、前より少し画像らしくなったが、まだ化けている。画像は縦に拡大され、一部しか出ていない。

 あ、あ、あ、わかったぞ。データレングスだ。やっぱりデータサイズはバイト数ではなく、データ数だ。今までは、1行当たりに2倍のデータを送っていたのだ(画像が縦に拡大される)。

 DMAに時間がかかっていたのも2倍のデータ転送をしていたからだ。転送終了を待っている時間が0でなかったのは、最初の1行の時だけの待ち時間だ。すべてのこれまでの現象がこれを裏づける。間違いない。あせる手で、ソースを修正する。ビルドする。終わるのが待ち遠しい。終わった。画像を出してみる。

 やった、やった。やりました。DMAを通してやっと綺麗なJPEG画像が出た。DMAのステイタスビットは明示的にクリアしないと、初期化では元に戻らないのだ。2つバグが重なっていたためにわけがわからなくなっていた。いやあDMAを動かすのにこんなに手順が沢山必要とは思ってもいなかった。

 夕食に、珍しくビールをだして家族と乾杯する。家族も良く分からないまま祝福してくれた。酒を飲むと、そのあと何もしたくなくなるので最近は殆ど飲まないが、今日は嬉しいのであとはどうでもよい。

Ws1_21_2011

STM32でSPIをDMAで動かす方法(1/16/2011)
 ソースコードは全体が出来てから公開しようと考えていたが、とりあえず、SPIでDMAを動かそうとしておられる方の参考のために、ノウハウをまとめておくことにする。

 これからご紹介するソースコードのオリジナルはここで、すべてSTマイクロの標準ペリフェラルライブラリを使うことを前提としている。レジスター単位に設定する方法もあるが、今後の運用性や、移植性を考えると面倒でも、こちらの方が便利だろう。

・まず、関数は2つ用意し、初期設定のルーチンを、DMA_SPI2_Init()、DMAを動かす関数を、DMA_SPI2_Transfer()とする。  (名前は任意。DMA化するSPIはSPI2)

・両方の関数が使用するDMAの初期設定構造体をグローバル変数

 volatile static DMA_InitTypeDef DMA_InitStructure;

で定義しておく。

・初期化ルーチンでは、以下のように初期設定をしていく。

void DMA_SPI2_Init(BYTE *data, uint32_t size)
{

//--------------------------------------------------------------
// DMA1のクロック有効化(ARMは省電力化のため最初はクロックを止めている)
// DMA1: AHBに属する。
//--------------------------------------------------------------
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE);
//--------------------------------------------------------------
// SPI2 TXイベントからのDMA要求はチャネル5(始めから決まっている)
//--------------------------------------------------------------
DMA_DeInit( DMA1_Channel5);   // 念のため初期設定値をクリアする

// SPI2のデータレジスタをペリフェラル側にする
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR);

// メッセージをメモリ側にする    
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) data;

// ペリフェラルを転送先にする
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

// 転送データ(RGB565の1行分)を設定。16ビットのデータ数であること
DMA_InitStructure.DMA_BufferSize = size ;

// ペリフェラル側インクリメントは不要
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

// メモリ側インクリメント使用   
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

// ペリフェラル側データサイズ16ビット
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;

// メモリ側データサイズ16ビット
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

// サーキュラモード(転送が終わると、最初のアドレスへ戻る)不使用
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

// 優先度: 最高
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

// メモリ間転送は不使用
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

// 以上の設定値で、DMAチャネルを初期化する。
  DMA_Init( DMA1_Channel5, &DMA_InitStructure);

// SPI2にDMAで動かすことを知らせる。なお初期化ではDMAをENABLEにしない
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);

} 
 // DMAの初期化の終わり。DMAを使う前に実行する。

・実際にDMAを動かす関数は、以下のようにし、処理ループの必要なところでこれを呼ぶ。

void DMA_SPI2_Transfer(void)
{DMA_Cmd( DMA1_Channel5, DISABLE);          // 一旦、DISABLEにする
DMA_Init( DMA1_Channel5, &DMA_InitStructure);  // 初期化(必要)
DMA_ClearFlag(DMA1_FLAG_TC5 | DMA1_FLAG_HT5); //2つともクリア
DMA_Cmd( DMA1_Channel5, ENABLE);        // ここでDMA転送が始まる
}

・DMAは必ず並行処理になるので、DMAが終わったかどうか調べる必要がある。処理ループ内で次のステートメントでDMAの処理の状態をチェックする。これはフラグだが、これ以外にも割り込みで処理する方法もある。

while(DMA_GetFlagStatus(DMA1_FLAG_HT5)== RESET); //HT5..半分TC5.. 完了

・以上で、SPIはDMAを通してデータが送信されるようになる(受信は、全く別のチャネルで新たに設定)。ARMのDMAは、メモリ、ペリフェラル間での多彩な機能を持っているだけに、これを使いこなすのは逆に大変である。なお、SPI側の設定は全く変える必要はない。

・ペリフェラルの種類と転送先で使うDMAチャネルは一意に決まっている(マニュアルに詳しく出ている)。ペリフェラルによっては、DMAを使うということを知らせる必要があり、SPIでは上記のコマンドが必要であるが、必要でないペリフェラルもある。

・また、SPIの場合は、ENABLEにした途端にスタートするが、次のスタートには必ずDISABLEにして再度、ENABLEにしないと始まらない(ここがはまる)。DMAのスタートは、これ以外に色々な方法がある(タイマー、割り込み)。ソフトで開始を始められるペリフェラルもある(ADCなど)。

・さらにDMAステイタスビットは、初期化などではリセットされない。必ずユーザーが明示的にクリアしないとおかしくなる(ここもはまりやすい)。

・データレングスは、バイト数ではない。転送単位のデータ数であることに注意。

 気になる処理時間だが、残念ながらDMAによって飛躍的には早くならなかった。DMAによるSPIは、ほぼスペックどおりの速さ(9Mbps)で、CPUを介して動かすときと大差ない。実測すると、JPEGのデコードは1行が2ms程度で、JPEGのデコードが予想以上に早く、全体では200ms程度しか改善されなかった(1.5~1.6秒)。まあ、動かすことが目的だったから、満足しよう。

液晶モニターの電源をDC-DCコンバーターにする(1/18/2011)

 最終のゴール、第9ステップは、FPGA基板やCPU基板を液晶モニターケースに固定して、フォトフレームとして完成させることである。基板は、ケースに入らないので、外側に固定する。あまり見栄えはよくないが、自作フォトフレームなのだから許してもらおう。

 それより電源が問題である。このあいだ液晶用の12V電源から降圧した5Vをケースの外に出してFPGAを動かしてみたが、5分もたたないうちに電源が不安定になりFPGAが誤動作した。

 FPGA基板の電流消費は、150mA以上あり、12Vから5Vに落とす際の消費熱量がレギュレーターの限界を超えているようだ。熱抵抗を計算してみるまでもなさそうである。ヒートシンクをつければ良いのだろうが、それより12Vからただ発熱させて5Vにするのは実に無駄である。といって、たかがフォトフレームくらいでDCアダプターを2つも使うのも非現実的だ。

 そこで、思いついたのが、ストロベリーリナックスで面白がって買ったDC-DCコンバーターである。5V入力で、9~20Vを出せる基板(¥945)である。9Vバッテリー(006P)をリチウム充電池で作ろうと買ってあった。少し高かったが、こいつ小さくても出力電流は500mA以上とれる。液晶モニターのインバータ電源(12Vで実測300mA)にしても問題ないはずだ。

P1183607

 早速、基板にピンを立ててブレッドボードでテストした。最初、ブレッドボード付属の5Vアダプターが昔の0.36Aしかとれないやつで、負荷をかけると電圧が上がらず、点灯しなかったが、2Aのアダプターでテストすると問題なく動作した。よし、これで電源周りは効率良く(DC-DCコンバーターの効率は90%近いので発熱もない)なった。

  今のところ、ディスプレイのインバーターが5V換算で0.7A、ロジックが0.13A、FPGAが0.15A、STM32が0.04Aなので、合計で1A少々。 2A程度のアダプターなら全部をまかなっても余裕だ。

 液晶モニターのコネクター基板の工作を始めた。ついでにBMP画像を出すことをあきらめて、JPEGのため画像のスキャンを下から上に戻す修正もする。DC-DCコンバーターがコネクター基板に載らないので、汎用基板の小片を追加してハンダ付けする。

P1183592

 レギュレーターをはずして配線を変更する。前より簡単になった。通電。うまく画像が出た。この液晶モニターは電源電圧が9Vでも動く。コンバーターの容量が心配なので、電源電圧を10Vに下げて設定し、1時間ほど連続して動かしてみる。問題なさそうだ。

コンバーターのICとコイルは少し熱いが、ずっと持っていても大丈夫なくらいの発熱で、大したことはない。FPGAにP1203621電源を供給しても全く問題なし。ただし、アダプター本体が少し熱を持つ。アダプターはこのあいだトラブルを起こした例の秋月の電源アダプターだが、何といっても秋月のは品揃えも多く小型で安価なので他を使う気がしない。2.3Aを買ってきたが、もう少し大きい3Aくらいが必要なようだ。

| | コメント (0) | トラックバック (0)

2011年1月11日 (火)

フォトフレーム:JPEG画像の出力に成功。しかし問題山積

みなさま新年おめでとうございます。
 当研究所はブログの公開から足掛け3年、電子工作そのものは始めてもう4年になりました。AVR研究所という名前がついておりますが、最近はFPGAやARMばかりでAVRを使う機会が少なく、そろそろ名前を変えなければいけません。

 AVRをやり始めてからAVRが急速に普及し、特にArduinoの人気に支えられて扱うお店が飛躍的に増えました。今開発の中心になっているARMプロセッサーSTM32も雑誌の付録でいじっている2年前はマイナーでしたけど、最近は何故か話題になることが多くなり、参考書の種類も増え、手に入るCPU基板も多様になってきました。

 自分が気に入って選んだディバイスに人気が出て扱う人が増えるというのは何となく嬉しいものですが、当研究所は、余り個別のハードウエアにはこだわらないことにしています。AVR研究所という名前も、電子工作をAVRで始めたというだけのことで、それ以上の思いはありません。あくまでも実用、アプリケーションがあってこそのハードウエアというのが基本の考えです。

 ただ、フォトフレームや、BeagleBoardを触っていて感じたことですが、今の電子工作、少し凝っていくと、殆どが市販のPC(それも古い)や10年前の携帯端末の機能に限りなく近づいていくだけで面白みがありません。

 そういう意味で、当研究所では、これからは、なるべくこうした機器の機能とぶつからない分野に狙いを定めて工作を考えていこうと思っています。よろしくお願いいたします。

 それはさておき、今年のお正月も電子工作で明け暮れていました。先日、懸案のJPEGが動き出しほっとしたところです。年末からの作業日誌をご紹介します。

P1093581

LAN電源コントローラーの修理(12/29/2010)
 3年前に作ったマイクロチップ社のNICチップENC28J60を使ったプリンターの電源を遠隔制御するコントローラーが時々誤動作するようになった。このディバイス(ブログ2008年8月29日「LAN電源制御成功!」)は、がた老AVR研究所が作った機械の中では最高の働き者で、地下の工作室にあるレーザープリンターを2階や1階のパソコンからネットを通して電源の入り切りが出来るので評判が良い。完成以来ずっと電源は入ったまま全く故障もせずに元気に稼動してきた。

 ところが、ここ数ヶ月前から、たまに誤動作するようになった。時々ネット上で行方不明になる。リセットすると正常になるので放ってあったが、最近はプリンターの電源が入ったとたんにコントローラーがリセットされプリンターの電源がすぐ切れてしまうようになった。うまく入るときもあるが、これでは役に立たない。家族の苦情も出てきた。症状から見て、電源不良の疑いが濃い。

A4111243

 また例の3端子レギュレーターか。プリンターの大電流が流れる時に一瞬、CPUの電源電圧が低下し、リセットしてしまうのだろう。電圧ロガーのようなものがあれば良いが、症状の出ているときを見計らって(暫くプリンターを動かさないときに起きやすい)、ケースをはずして電圧を測ってみた。3年前の作品である。構成などすっかり忘れている。あれえ、問題のレギュレーターはLANチップの3.3V用しか使っていない。CPUの電源は秋月の5Vアダプター直付けだ。電圧を測ってみる。4.5V。うーむ、この程度ではリセットしないはずだが。

P1093582

 と、ケースの中にあるACアダプターのモールドが割れているのを見つけた。中味が見える。何だ何だ。何故モールドが割れているのだ。割った覚えはない。熱で割れたのだろうか。中を見ると、見事に電解コンデンサーの一ヶのトップが膨れている。おおー、これに違いない。これを交換すれば動くかもしれない。とりあえずは予備のアダプターと交換してみよう。

P1063565

別の5Vアダプターをつけてテストしてみる。よーし全く問題なく稼動する。やっぱり予想通り電源が故障の原因だった。予備の5Vアダプターのケーブルを切り、このコントローラーの電源に作り替える。取り外した故障のアダプターの出力電圧をオシロで調べてみた。無負荷でも3Vくらいのところから、激しい脈流になっている。平均では4.5Vでも、これでは正常に動かないはずだ。とりだしたコンデンサーは容量が1/3の300μFに低下していた。 密閉機器に入っていたから、熱が原因かもしれないが、良く見るとこのコンデンサーの耐圧は6.3Vしかない。温度は105度まで耐えられるようだが5V出力なのにこの耐圧では問題が起きるはずだ。

P1063560

JPEGライブラリの実装に苦労する(1/3/2011)
 年末・年始の行事も一段落して、電子工作に時間をかけられるようになった。フォトフレームプロジェクトの第8ステップにあたるJPEGライブラリの実装を暮から始めている。

 今までのIJGのソースをモディファイしようと準備していたが、とっかかりがなかなか見つからず困っていた。しかし前回の記事のようにsirius506さんのソース(MP3Player)を雑誌のダウンロードページで見つけ、JPEGデコードの道筋がついた。

 このソースは、こちらが意図しているのと同じJPEGからビットマップへの伸張(IJGではデコードと呼ばずdecompressと言う)だけなので、ライブラリの量が少なくて助かる。JPEGライブラリと、ユーザーインターフェースのモジュールをソースを読みながら選択してEclipseにとりこむ。

 彼のブログの記事を参考に、少しづつ移植作業を開始する。現在のBMPファイルを表示する本体プログラムに機能を追加していく。彼の記事のいう変更点はすべて修正が済んでいた(当たり前か)。Makefileにもモジュールを追加登録する。

 移植のコツというのがあるとすれば、全体を通して流れが確認できたら、とりあえずエラーを恐れずにコンパイルをしてみることだ。あまり周到に調べていってもきりがない。エラーはあとから少しづつつぶせる。

 最初のビルド。案の定、山ほどコンパイルエラーが出る。これは想定済みである。ひとつづつエラーメッセージを読み個別に解決していく。時間計測をTOPPERS/JSPのサービス関数でやっているので、当面はすべてコメントアウトする。SYSLOGなどのコードは、はずすか、UARTにメッセージを出すコードに置き換えていく。

 やっかいだったのが、boolタイプの定義でコンパイルエラーが出ていることである。このエラーはWebで調べてもはっきりしない。そもそも以前からオリジナルのコードにも「ねむい」さんの修正が入っている。エラーメッセージは予約語を二重定義していることを怒っているようなので、思い切って定義そのものを削除する。何故か、これで上手く行った。(未定義エラーのときは適当に設定。falseとtrueの2つだけなので余り心配ない)

 出力が問題だ。Sirius506さんのオリジナルはLCDに出している。ソースを調べているうち意外な機能を発見した。ソースコードを読む限りは、このライブラリは入出力のピクセルサイズを変えられるような感じがする。スケーラーがついていて1/8単位で縮めることが出来るようだ。良かった。これで大抵のJPEGファイルは何もしなくても表示ができそうだ(このあとreadmeファイルでも確認した)。

 出力は、今度のSPIはDMAを使ってやろうと思っているので、当面、UARTにただ16進出力を出し続けるコードにする。出てくる警告は無視し、エラーだけをつぶして行く。遂にビルドはNO ERRORとなった。フラッシュのサイズは80KBくらい。こんどの石は512KBあるので楽勝だ。

DMAが動かない(1/6/2011)
 コンパイルが通ったので、久しぶりにFPGAとSTM32基板、液晶モニターを組み立てて電源を入れてみる。おやあ、画面の上部に縞模様がでて画像が乱れている。SRAMアクセスがうまく行っていないようだ。これの原因究明までやっている余裕はない。JPEGのテストが先だ。

 「ねむい」さんから頂いたDFUSeで快調にファーム書き込みを終える。いちいちDFUManagerを動かしてDFUファイルに変換する手間が省けて効率が良い。「ねむい」さんありがとう。

 BMPファイルのブラウズのルーチンに間借りする形でJPEGデコードが動くようになっている。JPEGファイルをいくつかSDカードに入れてテスト。画面には出てこないがUARTに16進ダンプが流れる。

 よし、暴走せずに終わった。ふむふむ、このデコードは1行単位のようだ。頭の部分しか表示していないので何ともいえないが、ビットマップデータらしいコードが出ている。同じデータでなく変化しているので、JPEGデコードはうまくいっているようだ。

 このプログラムのもともとはChaNさんのFatFSのARM版である。ChaNさんのFatFSには速度を測るタイマーがついているのを思い出した。調べてみる。あった。これは1ms単位で測れるルーチンで、ARMではsystickを使って実装している。これを最初コメントアウトした時間計測のルーチンに使わせてもらう。

 640×480のJPEGデコードは、900msくらいかかっていることがわかった。これまでのBMPファイルでの送出が、2秒近辺なので、あわせると3秒。うーむ、やっぱりDMAを使いたいな。JPEGがデコードし終わってから、SPIで送るのだが、DMAを使えば、SPIの転送とJPEGデコードをかぶらせて処理時間を短縮できる。

 えらそうなことは言っているが、当研究所ではDMAの実装は始めてである。大型コンピューター時代から理屈はいやというほど知っているが(大型機ではチャネルと呼んでいた)、実際に動かしたことはない。8ビットのAVRにはない機能で前からマスターしたいと思っていたが機会がなかった。

 STM32のDMAを調べ始めた。ここが一番詳しい。しかし、SPIの例が見つからない。設定はわかるが、どうして動かし始めるのかが良く分からない。サンプルコードはタイマーの割り込みがスタートになっている。SPIの送信は何がトリガーになるのだろう。

 こういうのは動かしてみるのが一番だ。見よう見まねで、コードを掻き集め、コーディングをすませて実際にテストしてみる。しかし、ピクリとも動かない。オシロで見てみるが全くSPIは動いていない。

 こうなると意地になるのが癖である。片っ端からウェブでDMAを勉強する。わからない。暫く経ってから、突然、本体のFatFSがDMAを使用していることに気づいた。あわててソースを読み込む。ふむふむ、DMA転送の度に、初期化をしている。おお、ひとつコマンドが抜けていた。SPI_I2S_DMACmd()というコマンドがSPIとDMAをつなぐ。そうだよな。おかしいと思った。

P1113583

 勇躍、コードを入れ替えテストする。よーし、何か画面がでたぞ。バッファーがずれているようで縞模様にしかならないが、やっとSPIがDMAで動いた。嬉しい。あとはデータの位置を直せば画面が出るだろう。それにしてもDMAは大変だ。

やっとJPEGで画像が出た(1/8/2011)
 DMAは動いたが、まともな画像が出てこない。JPEGライブラリが本当に画像をデコードしているのか確かめないことにはDMAが悪いのかどうかはわからない。で、DMAの開発を一時中断し、時間はかかるがSPIでJPEGデコードデータを検証することにした。

 JPEGは3バイトのフルカラービットマップで出てくる。これを16ビットRGBに変換してやる必要があるが、これはすでにBMPのところでやっている。それをそっくり持ち込んでJPEGをSPIで出すコードはすぐ出来た。

 テストする。おおお、確かに画像が出た。BMPとスキャンが逆なので(BMPは下から、JPEGは上から)、画像が天地さかさまで、画面上部が例のSRAMトラブルで乱れているが、きれいな画像データが表示されている。

P1083566

 いやあ感激の一瞬である。FPGAを使ったフォトフレームプロジェクトは遂に、第8ステップをクリアした。ARMのJPEGルーチンがやっとのことで動いたのだ。いくつかJPEGファイルを入れてみる。1024×768のファイルも問題なく表示された。スケーリングもしている。

 それに何となくDMAを使って表示しているときより早いような気がする。DMAからはまだまともな画像にはなっていないが、こんなに早くはなかった。一息ついていた。気になったので、DMAに戻して例の時間計測ルーチンをファイルの読み込みから画像を出すまでに入れなおして時間を測ってみた。

 何と何と、結果を見て驚いた。勘はあたっていた。DMA経由より、SPI単独の方が早いのだ。3つのJPEGファイルでテストしているが、どのファイルもDMA転送では、2.1秒近辺なのに対し、DMAなしが1.7秒程度と、20%以上も早い。ついでにBMPファイルでの表示時間も測ってみる。これは2秒前後であった。何だとDMAの方が遅いじゃないか。

 JPEGが早いのは、読み込むファイルのサイズが1/4しかないことが大きな理由であろう。DMAが遅いわけは、まだ良くわからない。DMAルーチンは元のFatFSのコードを参考にしておりDMA転送の度に初期化をし(Deinit)、設定し直して動かしている。この時間が長くかかりすぎているのかもしれない(Deinitをはずすと動かない)。

 次のDMA転送のために全部のデータを送り終えるまで待つようにしているのをバッファーの半分で開始するようにしたら

while(DMA_GetFlagStatus(DMA1_FLAG_HT5)== RESET); // TC5からHT5にする

2秒から1.7秒程度に縮まった。しかしこれでもSPIで送るシリアル処理と同じ程度だ。DMAの意味がない。それにDMAはまだまともな画像が出ない。

P1093579

 ソースコードはまだぐちゃぐちゃだし、BMPとJPEGの画像の天地の問題も解決されていないし、画像の上部には縞模様が出たまま(これはあとで接触不良が判明し解決した)で、問題山積というところだが、まあ、そうは言っても、フォトフレームプロジェクトは、第9ステップの実装を残すところまで来た。このあたりでブログに報告することにしよう。
(追記:上記のDMAの問題は、すべてこちらのコーディングミスでした。後記のブログ記事参照)

| | コメント (2) | トラックバック (0)

« 2010年12月 | トップページ | 2011年2月 »