« 2011年4月 | トップページ | 2011年7月 »

2011年5月の記事

LPCXpresso LPC1769でFatFsを動かす - DMA編

前回の延長で、FatFsをDMAモードで動かしました。オリジナルコードをベースにDMA完了割り込み処理を修正する程度で動いたのですが、そこからDMAの最適化を試みるも敢え無く敗退でした。


動作概要

DMA処理の概要は以下の通りです(オリジナルコードの処理です):

<SD Cardからの読み出し>

  • SDカードとのインターフェースにはSPIを使用しますが、SPIは以下の図に示すとおり、Master (LPC1769)から①ダミーデータ(今回は0xFFを使用)を送信することによって、②Slave (SD Card)のデータ送信を促します

SPI_DataTransfer2

  • そのため、ダミーデータの送信用に0xFFを512個(一回のDMA転送で読み出すデータ量)設定した配列を宣言。データは固定値のためROMに配置
  • DMA_CH0を上記①のダミーデータ送信用として使用
  • DMA_CH1を②の読み出しデータ受信用に使用
  • DMAを起動し、完了割り込みを待つ

<SD Cardへの書き込み>

  • 読み出しの逆で、①送信バッファには書き込みデータを設定。データを書き込むと②ダミーデータを受信します
  • そのため、②ダミーデータ受信用のバッファを512バイト宣言
  • DMA_CH0を書き込みデータの送信用として使用
  • DMA_CH1をダミーデータの受信用に使用
  • DMAを起動し、完了割り込みを待つ

動作概要を見ていただくと分かるのですが、DMAの半分はダミーデータの送受信に使っておりなんとなく無駄が多いです。そこで、以下の最適化にチャレンジしました。


DMA処理の最適化

最適化1:片方向のDMA

SD Card読み出しではCH1しか有効データの処理に使っていないのだから、CH0のDMA送信を止めてしまえという無謀なアイデア。しかし、SPIの仕様としてマスターからダミーデータを送らない限りSD Cardからのデータが出てこないので、敢え無くボツ


最適化2:ダミーデータ用バッファをなくす

送信用のダミーデータは広大なROMに配置すればよいので容量的には気にならないのですが、ダミーデータ受信用のRAMに512バイト無駄使いするのはちともったいないです(まあ、64KBあるのでけちなことを言うなですが・・)。あと、余計なデータ書き込みがあるとバス帯域を消費して転送性能にも影響が出そうです。オリジナルコードの作者さんは、この点を改善しようとして挫折した痕跡がコードに残っていました。

LPC17xx GPDMAの機能として、転送のDestinationバッファのポインタを自動インクリメントする・しないを指定するフラグDI(Destination increment)がDMACCxControlレジスタ内に存在します。CMSISライブラリのGPDMA_Setup() 関数を使ってPeripheral to MemoryのDMAをセットアップするとライブラリ内でDIを有効に設定するのですが、受信データがダミーになる場合はGPDMA_Setup()を呼んだ後でDIを無効にすることによって、ダミーデータを配列に格納せず読み捨てることができないかというものです。受信したダミーデータを汎用レジスタに書き込むだけでRAMを使わないようにすれば、メモリー帯域の節約にもなりそうです。

しなしながら、オリジナルコードのコメントにもありますが、DIを後付で無効にすると、DMA完了割り込みが発生してくれません。原因は分からないのですが、残念ながらボツです。


最適化3:バーストサイズの拡張

GPDMA_Setup() 使ってDMAの初期設定を行うと、Memory - SPI間のDMAではバーストサイズが4byteに設定されます。すなわち、4byte転送する毎にDMACがバスマスタを一旦放棄してMCUが動ける隙間を作ります。リアルタイムOSを使用する場合は、DMAがMCUを占有しないという点でこれくらいのバーストサイズが適切なのかと思いますが、頻繁にバスマスターの遷移を行うせいか、前回行ったソフトベースの転送に比べて若干性能が劣ります(転送性能の詳細は次項に記載)。そのため、バーストサイズの拡大を試みました。

バーストサイズの設定は、GPDMA_Setup() 内でDMACCxControlレジスタのSBSize, DBSizeを設定することで行っているため、ライブラリのコードを変更して強制的に8byteのバーストサイズを設定してみました。結果はP2M(SD Cardからの読み出し)時に、DMA_CH0(ダミーデータの送信)はDMA完了割り込みが発生するのですが、DMA_CH1(SD Cardからの受信データー)の完了割り込みが発生してくれず、NGでした。

SPIクロックを下げる、バッファのアライメントをバーストサイズ境界にする(8byteバーストの場合、DWORD境界に配置)など試したのですが解決せず、残念ながらボツです。


最適化4:ダミーデータ用RAMの配置移動

オリジナルコードではDMA処理関数内のローカル変数として512byteのダミーデータ用配列を宣言しています。すなわち、スタックにダミーデータを取るため、0x1000 0000~0x1000 7FFFのメインSRAMを消費することになります。この領域を使うのはもったいないので、0x2007 C000~0x2008 3FFFのAHB SRAMにバッファを配置してみました。せめてもの最適化ということで・・ ダミーデータバッファの宣言に __attribute__ ((section (".bss.$RAM2*"))) を付けることで配置を制御しています。

結局最適化は動かず、最適化4以外はオリジナルコードのまま(CMSISライブラリv3対応の書き換えのみ実施)となりました。ソースは以下のリンクから:


動作確認結果

以下の通り性能測定を行いました。条件は、前回のソフト転送モードと同等です。

Hello from the ChaN FatFs Demo on LPC1700
Version 0.0.2, Martin Thomas 7/2010
xprintf is working
CPU Clock:        120MHz
Peripheral Clock: 60MHz
SPI Clock:        30MHz

FatFs module test monitor for LPC17xx/SSP
LFN Enabled, Code page: 1252

>di 0
rc=0
>fi 0
rc=0 FR_OK
>
>fo 10 test. 1.txt
rc=0 FR_OK
>
>fw 10000000 30
10000000 bytes written with 833 kB/sec.
>fw 10000000 31
10000000 bytes written with 465 kB/sec.
>fw 10000000 32
10000000 bytes written with 526 kB/sec.
>fw 10000000 33
10000000 bytes written with 468 kB/sec.
>fw 10000000 34
10000000 bytes written with 587 kB/sec.
>
>fc
rc=0 FR_OK
>fl
----A 2010/07/15 11:57  50000000  test1.txt     
   1 File(s),  50000000 bytes total
   0 Dir(s), 1912852480 bytes free
>
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2149 kB/sec.
>fr 10000000
10000000 bytes read with 1483 kB/sec.
>fr 10000000
10000000 bytes read with 1541 kB/sec.
>fr 10000000
10000000 bytes read with 1543 kB/sec.
>fc
rc=0 FR_OK
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2149 kB/sec.
>

結果は、書き込みではソフト転送とほぼ同じ性能が出ていますが、読み出しでは7~10%程度劣る結果となりました。たぶんバーストサイズが小さいことが響いているのではと思うのですが。


おわりに

人様のコードベースではありますが、DMAまでなんとか動作。最適化については敢え無く敗退でした。最適化2のDI無効化は強引感がありますが、最適化3のバーストサイズ拡大は動いてもよさそうなんですが。DMACの設定だけでなく、SSP(SPI)側の設定とか何か抜けているところがあるのか。CMSISライブラリのお仕着せパターンに従った設定ベースでの使用ならなんとかなるのですが、それを超える個別パターンになるといきなりハードルが高くなる感じです。

LPCXpresso LPC1769でFatFsを動かす

前回に引き続き、LPCXpresso + MARY-XBを使って、micro SDカードとFatFsを使えるようにしました。Low level I/Oの部分は、Interfacing ARM controllers with Memory-Cardsの実装をベースにしました。


ソースの入手と変更点

ソースコードは冒頭のリンクから、ChaN's FAT-code with interface for NXP LPC17xx (LPC1766, LPC1768 and others)をたどってダウンロードします。

このソースはOlimex LPC1766-STKがターゲットになっておりSSP1を使っていますが、今回micro SDカードをSSP0に接続しているため、Low level I/OのSSP定義部分だけを変更すれば簡単に動作しそうです。Low level I/OにCMSIS Peripheral Libraryを使っておりダウンロードしたzipにも入っているのですが、バージョンがv1.0とずいぶん古いです。人様の成果をそのまま使うのも何なので、この部分だけ最新のv3.0を使うことにしました(ついでに、Low level I/O codeの学習も兼ねて)。

v3.0ではオリジナルコードで使っている旧APIが削除されたりしており、部分的に書き換えが発生しました。以下変更箇所の概要です。

  • comm.c(コンソール出力用のUARTドライバ):
    割り込みハンドラー内でライブラリが提供しているUART0_StdIntHandler()を呼び出して、割り込み要因毎の処理(送信やら受信)はUART_SetupCbs()関数を使ってCallback関数を登録する方法になっていますが、最新のCMSISライブラリではUART_SetupCbs()関数が削除されています。そのため、割り込みハンドラー内で割り込み要因別の処理を書く形にしました
  • spi_sd_lpc17xx.c(SPIの制御とLow Level I/Oを受け持つ部分):
    使用するSSPポートの変更(SSP1→ SSP0)に加えて、SSPの初期化関連でLibraryの差し替えに伴う変更がありました。SDカードの初期化とその後のファイルI/OでSPIクロックを変えるために、オリジナルはSSP_SetClock()関数を呼んでいますが、v3.0ではpublicな関数として定義されていいません(名前も変わっている)。そのため、SSP_Init()を呼ぶ形で対応。SSP_Init()を呼ぶとSSPのEnable bitがクリアされてしまうため、SSP_Cmd(LPC_SSP0, ENABLE)を再度呼ぶ必要があります。

変更を行った箇所を含むファイル一式は以下です。


プロジェクトのビルド

LPC1769_FatFS.zipをダウンロードして、workspaceにインポートを行います。CMSISライブラリは含んでいないため個別に用意して下さい。最新のCMSISは、このリンクからLPC17xx CMSIS-Compliant Standard Peripheral Firmware Driver Libraryをダウンロードします。

最新版はCore Libraryがv2.01になっているため、LPCXpresso IDEに別のCMSIS Libraryを作っていたのですが、新規プロジェクトを作るとデフォルトの参照先がIDE組み込みのv1.30になってしまい毎回変更が面倒です。そのため、workspace\CMSISv1p30_LPC17xxにPeripheral Library v3.0をコピーします。

  • system_LPC17xx.c → IDE添付版の方が版数が進んでいるため、IDE添付版を使用
  • LPC1700CMSIS\Drivers\includeフォルダ配下のファイル一式 → workspace\CMSISv1p30_LPC17xx\inc
  • LPC1700CMSIS\Drivers\sourceフォルダ配下のファイル一式 → workspace\CMSISv1p30_LPC17xx\src


動作確認

対話式のコマンドシェルから操作を行って、ディスクのマウント→ ファイルの書き込み → 読み出しの試験を行いました。SPIのクロックは30MHzにしています → Peripheral clock (pclk)の1/2

動作確認時のログは以下の通りです。

Hello from the ChaN FatFs Demo on LPC1700
Version 0.0.2, Martin Thomas 7/2010
xprintf is working
CPU Clock:        120MHz
Peripheral Clock: 60MHz
SPI Clock:        30MHz

FatFs module test monitor for LPC17xx/SSP
LFN Enabled, Code page: 1252

>di 0
rc=0
>fi 0
rc=0 FR_OK
>fs
FAT type = FAT32
Bytes/Cluster = 4096
Number of FATs = 2
Root DIR entries = 0
Sectors/FAT = 3744
Number of clusters = 479214
FAT start (lba) = 841
DIR start (lba,clustor) = 2
Data start (lba) = 8329

...0 files, 0 bytes.
0 folders.
1916856 KB total disk space.
1916852 KB available.
>
>fo 10 test1.txt
rc=0 FR_OK
>
>fw 10000000 30
10000000 bytes written with 866 kB/sec.
>fw 10000000 31
10000000 bytes written with 471 kB/sec.
>fw 10000000 32
10000000 bytes written with 535 kB/sec.
>fw 10000000 33
10000000 bytes written with 478 kB/sec.
>fw 10000000 34
10000000 bytes written with 606 kB/sec.
>
>fc
rc=0 FR_OK
>fl
----A 2010/07/15 11:59  50000000  test1.txt     
   1 File(s),  50000000 bytes total
   0 Dir(s), 1912852480 bytes free
>
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 10000000
10000000 bytes read with 2369 kB/sec.
>fr 10000000
10000000 bytes read with 1581 kB/sec.
>fr 10000000
10000000 bytes read with 1655 kB/sec.
>fr 10000000
10000000 bytes read with 1657 kB/sec.
>fr 1    
>fc
rc=0 FR_OK
>fo 1 test1.txt
rc=0 FR_OK
>
>fr 30000000
30000000 bytes read with 2371 kB/sec.
  • 34行目で10MBの文字(文字コード30)を書き込み → 転送レート866KB/sec
  • 36行目以降で、10MB毎に追加書き込みを行うと転送レートが増減します
  • 55行目で、10MBの読み出しを実施 → 転送レート2369KB/sec
  • 57行目以降で、10MB毎に追加読み出しを行うと、書き込み同様に転送レートが低下
  • 64行目でファイルを一旦クローズして新規に読み出しを行うと元通りの転送レートになります


おわりに

転送はFIFOモードでDMAは使っていません。DMAのコードも作りこまれているのですが、"does not work yet"のコメントが入っていたりしてまだ動かないようです。コンパイラオプションでDMAを有効にできますが、割り込みハンドラーが旧CMSIS Library v1.0をベースにしているためそのままでは動きません。DMAの有効化は今後の課題です。

追記:DMAハンドラーを最新CMSISにあわせて修正したらDMA版も動作してくれました。DMA版は未完成という訳ではなく、メモリ上のデータをSSP_TxポートからSDカードに転送する際に、現状はSSP_Rxポートで受信したデータをダミーのバッファーに格納しているのですがこれを読み飛ばす処理がまだできていないようです。(メモリーリードに加えてライトが発生するため性能に影響があると思われます)

DMAを使わないFIFO転送ですが、転送レートはそこそこ出ていると思います。
追記:DMA版はダミーデータの読み書きを行うせいか、FIFO版より若干転送性能が落ちました。

今回の移植は低レベル処理が主体だったので、SWD(JTAG)デバッグに助けられました。SPIクロック変更のためにSSP_Init()を呼ぶとSSPのEnable bitがクリアされる問題は、当初SPIが動かないように見えていたのですが原因が分からず、SSP0CR1レジスタの値を見ながらトレースして原因判明するなどデバッガが活躍してくれました。時々エミュレーターが起動できなくなってWindows再起動で復旧させることがあったのですが、このあたりがもうちょっと安定すると文句なしです。


参考資料

LPCXpresso LPC1769にMARY拡張基板を乗せる

トラ技別冊小型ARMマイコン基板(MARY)用の拡張基板をLPCXpresso LPC1769で使えるようにしました。MARY拡張基板のコンパクトさを生かして、OLEDディスプレイ基板(OB)と無線モジュール基板(XB)をLPCXpressoの上に乗っけてみました。

LPCXpresso_MARY

裏面の写真です。主にUEW線を使って配線しています。配線に加え、裏面にピンヘッダを取り付けてLPCXpressoとの結合を行っています。

IMG_0899.jpg

LPCXpressoとの接続

秋月さんの140x40mm両面スルーホール基板を使って、LPCXpressoの上に拡張基板を搭載しました。搭載モジュールと接続先は以下の通りです。   

搭載モジュール LPCXpressoの接続先
OLEDディスプレイ(OB基板) SSP1(P0.6~P0.9)
RESET(GPIO P0.23)
OLED_VCC_ON(GPIO P0.24)
加速度センサー(OB基板) I2C1(P0.0~P0.1)
MEMS_INT(GPIO P0.25)
XBee(XB基板) UART3(P0.10~P0.11)
RESET(GPIO P2.0)
micro SDカード(XB基板) SSP0(P0.15~P0.18)
INSERT(GPIO P2.1)
USB-シリアル変換(FT232RL) UART0(P0.2~P0.3)
Ethernet J6-32~J6-35
ISPジャンパー P2.10 - GND
MCUリセットSW J6-4 - GND
外部電源(5V)コネクタ J6-2, J6-1

IDEからのプログラム転送ができなくなった際(例の" load failure: Et: Flash driver not ready"が出た場合)の復旧用に、ISPジャンパーとリセットSWは必須アイテムです。

LPCXpressoは5Vの出力ピン(mbedの5V USB out相当)がないため、OLEDなどの駆動用に外部の5V電源が必要です。電源コネクタの取り付けが今一でした(変換アダプタを使わずに、基板に穴を開けて直接半田付けした方がよかったな・・)。

LPC17xx用のドライバ

連休を費やしてXBee以外のモジュールは動作を確認できました。使用したドライバ類は以下の通りです。 

モジュール ドライバ
OLED MARY(LPC11xx)用のドライバを移植
加速度センサー(MEMS) MARY(LPC11xx)用のドライバを移植
micro SD / FatFs ChaN's FAT-Module with LPC17xx SPI/SSPを移植
Ethernet/TCP/IP nxp\lpcxpresso_3.6\Examples配下にインストールされるRDB1768cmsis_uIPを使用 
  • OLED/MEMSはLPC11xxとLPC17xxレジスタ関連の差分を直す程度で動作。ペリフェラルの初期化部分は最新のCMSIS Peripheral Libraryベースに書き換え
  • FatFsは Olimex LPC1766-STKがベースになっているところをLPCXpresso用に修正(SSP1→SSP0に変更など)。本来は、MARYドライバの移植より楽な筈ですが、そのまま拝借も芸がないため、最新のCMSIS Peripheral Libraryを使ったところ結構修正が出ました。オリジナルはCMSIS v1.0を使っている(ファイルも一緒に配布)のですが、最新のv3.0では廃止されたAPIがありその部分の書き換えが発生しました
  • uIPはLCD表示部分をOLEDに変更。uIP本体は無修正で動くため、移植は簡単

おわりに

久しぶりに基板の配線を行いましたが、配線ミスとの戦いでした。最初は、LPCXpressoのピン配置図(回路図の最後のページ)に搭載モジュールのピン番号を書くだけで作業を始めたのですが、ピン数が多いのと裏面配線のため作業がはかどらず、途中から裏面から見たピン収容図を画くことで対応しました。準備に時間がかかっても実体配線図などを作ってから作業に入ったほうが結果的に作業が早くなりますね。それでも以下のミスを連発してトホホでした。

  • Ethernetマグジャック(Sparkfanのピッチ変換基板を使用)とFT232RLをLPCXpresso J6コネクタ2本の間に乗っけたのですが、最初に直接半田付けした後で配線スペースがないこととに気がつく。おかげで結構窮屈な配線作業を強いられるはめに
  • マグジャックもSparkfanのものを使ったのですが、ピン配置を秋月のジャックで行ってしまう。Ethernetが繋がらないところまで行って、両者のピン配置が違うことに気がつきやり直し。パルストランス中点につけたコンデンサは再度取り付ける場所がなく割愛することに。まあ、線長が短い場合はコンデンサなしでも問題ないのですが・・
  • ISP用ピン(P2.10)の位置を間違う。J6-51に配線するべきところを、お隣のJ6-50に配線。何度か書き込みができなくなった際に、ひたすらリセットを繰り返していた。"Flash driver not ready"が出て不安定になっている状態でちゃんとISPモードに落ちていなくても、何回か繰り返すと書き込みに成功することがあったんですね。そのため配線ミスと分かるまで時間を要した

生のSPIやI2CをLPC17xxで触るのは始めてでしたが(mbedのSPIくらいは使ったことがありますが、あれはハードに直接触っているとは言えない抽象度なので)、こちらはそれほどはまることもなく通過できました。Arduinoで一通りはまる通過儀礼を行った甲斐があったというものでした。

各モジュールの動作確認結果は別途ポストします。

« 2011年4月 | トップページ | 2011年7月 »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ