ガレスタさんのDIY日記

電子回路、Python、組み込みシステム開発、自作エフェクターを語るblog

MIMXRT1052CVL5Bを使う際のTips

MIMXRT1052の場合 コンシューマ向けのは600MHz、工業用途の場合528MHz駆動となっている。
MCUXPressoIDEで新規プロジェクトを作った際にはMIMXRT1052DVL6B向けの設定で吐きだされる。
今回はMIMXRT1052CVL5B向けのプロジェクトの設定や変更しないといけな部分を書いておく。

正直いって工業用のSoMとか普通の人は買わないし評価ボード買って使うと思うんであくまで僕自身のメモTipsとして受け取ってくれればうれしいです。
使ってるボードはForlinxのFET1052-C核心板です。

f:id:gsmcustomeffects:20190222005316p:plain
RT1052のSoM

プロジェクトの作成

まずは普通にプロジェクトエクスプローラーを起動してプロジェクトを作ります。
新規プロジェクトはIDEの左下のメニューからNew projectから行えます。

f:id:gsmcustomeffects:20190222010306p:plain
プロジェクトの作成

チップの選択画面でMIMXRT1052CVL5Bに変更するのを忘れないでください。

Nextを押してAdvanced Settingにうつります。

f:id:gsmcustomeffects:20190222013530p:plain
メモリの設定
ここではメモリの大きさとフラッシュドライバの変更を行います。
画像を参考に設定をしてください。

プロジェクトを作成したらいったんビルドしてください

f:id:gsmcustomeffects:20190222013625p:plain
ビルドの結果

ビルドしてメモリが上記のようになっていればOK

SFDPってやつですがSERIAL FLASH DISCOVERABLE PARAMETERSの略でJEDECで規定されている内部パラメータテーブルです。
これに関しては補足しておくと

今までは外部フラッシュを用いるマイコンの場合はNXPがcfxファイルを配っていた。
しかし今回からはフラッシュメモリのSFDP内部レジスタから値を読み込んでflashのサイズだったり自動で解釈してやってくれるスクリプトに変わりました。
これにより多彩な外部フラッシュを提要可能になり設計者のコスト、機能を達成することができるようになるとのことです。
詳しく知りたい方はJEDEC SFDPでググったり、flash各社のデータシートを見るといいと思います。

XIP(eXecute-In-Place)の設定

次にブートに関する設定です。
IMXRT1052の場合内部にROMを持たないため外部フラッシュからブートする必要があります。
その機能のひとつとしてXIPという機能があります。
これはRAM転送なしに外部ROMから直接実行可能にする技術です。
最近では結構多くのマイコンでこの機能が備わっているのでデータシートをいろいろ読んでみると勉強になると思います。

そんなわけでXIPを使うためにはその設定をしないといけないわけです。
設定に必要なファイルはxipってフォルダにあります。
変更しないといけないファイルは

  • evkbimxrt1050_flexspi_nor_config.c
  • fsl_flexspi_nor_boot.h

の二つです。
SDRAMもメーカーが違うのでDCD部分も変更しないといけないんですが今回は使わないのと他社製のSDRAMのdcd配列吐く方法がいまいちわかっていないのでわかったらまた追記します。

evkbimxrt1050_flexspi_nor_config.cの変更

最初は評価ボードについているHyperFlashから起動するようにコンフィグが組まれているのでそれをQspi用に変更するだけです。
FLEXSPI_LUT_SEQってのがメモリの書き込み命令だったり、WriteEnableをトグルさせてます。
この辺の命令コマンドは各社違うので自分が使いたいメモリに合わせて選んであげる必要があります。
今回のはWinbondのメモリなのでこのデータシートの22ページのコマンドテーブルみつつ何してるか確認してみてください。
https://www.winbond.com/resource-files/w25q128jv%20spi%20revc%2011162016.pdf

//const flexspi_nor_config_t hyperflash_config = {
//    .memConfig =
//        {
//            .tag = FLEXSPI_CFG_BLK_TAG,
//            .version = FLEXSPI_CFG_BLK_VERSION,
//            .readSampleClkSrc = kFlexSPIReadSampleClk_ExternalInputFromDqsPad,
//            .csHoldTime = 3u,
//            .csSetupTime = 3u,
//            .columnAddressWidth = 3u,
//            // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock
//            .controllerMiscOption =
//                (1u << kFlexSpiMiscOffset_DdrModeEnable) | (1u << kFlexSpiMiscOffset_WordAddressableEnable) |
//                (1u << kFlexSpiMiscOffset_SafeConfigFreqEnable) | (1u << kFlexSpiMiscOffset_DiffClkEnable),
//            .sflashPadType = kSerialFlash_8Pads,
//            .serialClkFreq = kFlexSpiSerialClk_133MHz,
//            .sflashA1Size = 64u * 1024u * 1024u,
//            .dataValidTime = {16u, 16u},
//            .lookupTable =
//                {
//                    // Read LUTs
//                    FLEXSPI_LUT_SEQ(CMD_DDR, FLEXSPI_8PAD, 0xA0, RADDR_DDR, FLEXSPI_8PAD, 0x18),
//                    FLEXSPI_LUT_SEQ(CADDR_DDR, FLEXSPI_8PAD, 0x10, DUMMY_DDR, FLEXSPI_8PAD, 0x06),
//                    FLEXSPI_LUT_SEQ(READ_DDR, FLEXSPI_8PAD, 0x04, STOP, FLEXSPI_1PAD, 0x0),
//                },
//        },
//    .pageSize = 512u,
//    .sectorSize = 256u * 1024u,
//    .blockSize = 256u * 1024u,
//    .isUniformBlockSize = true,
//};

const flexspi_nor_config_t Qspiflash_config =
{
    .memConfig =
    {
        .tag = FLEXSPI_CFG_BLK_TAG,
        .version = FLEXSPI_CFG_BLK_VERSION,
        .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally,
        .csHoldTime = 3u,
        .csSetupTime = 3u,
        .deviceModeCfgEnable = true,
        .deviceModeType = 1,//Quad Enable command
        .deviceModeSeq.seqNum = 1,
        .deviceModeSeq.seqId = 4,
        .deviceModeArg = 0x000200,//Set QE
        .deviceType = kFlexSpiDeviceType_SerialNOR,
        .sflashPadType = kSerialFlash_4Pads,
        .serialClkFreq = kFlexSpiSerialClk_100MHz,//80MHz for Winbond, 100MHz for GD, 133MHz for ISSI
        .sflashA1Size = 16u * 1024u * 1024u,//4MBytes
        .dataValidTime = {16u, 16u},
        .lookupTable =
        {
//         //Fast Read Sequence
//         [0]  = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x0B, RADDR_SDR, FLEXSPI_1PAD, 0x18),
//         [1]  = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_1PAD, 0x08, READ_SDR, FLEXSPI_1PAD, 0x08),
//         [2]  = FLEXSPI_LUT_SEQ(JMP_ON_CS, 0, 0, 0, 0, 0),
           //Quad Input/output read sequence
           [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
           [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),
           [2] = FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0),
           //Read Status
           [1*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x04),
           //Write Enable
           [3*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, 0, 0),
           //Write status
           [4*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x2),
	 },
    },
    .pageSize = 256u,
    .sectorSize = 4u * 1024u,
};

fsl_flexspi_nor_boot.hの変更

flashのサイズがboard.hから呼ばれるようになっているのでそれを変更します。

f:id:gsmcustomeffects:20190222015331p:plain
フラッシュサイズの設定

クロックの設定

クロックツールを開きます。
そして先ほど作ったプロジェクトの設定をします。

f:id:gsmcustomeffects:20190222020331p:plain
クロックツールの設定

次にメインクロックを設定していきます。

f:id:gsmcustomeffects:20190222020726p:plain
クロックツールの設定2

次にグラフィカル画面にいきます。
そこでメインクロックの部分が青くハイライトされているのでいじれるとこのLOCKマークを解除して最終的なコアクロックのところに528MHz(MIMXRT1052CVL5Bでいう最大値)を入力します。

f:id:gsmcustomeffects:20190222021527p:plain
コアクロックの設定

するとツールが適切値を計算して出してくれます。(変更したくない部分がある場合はLOCKを忘れずに

f:id:gsmcustomeffects:20190222022230p:plain
設定値の確認

これでクロックの設定は終わりなので上のほうにあるUpdate Projectをクリックしてコードを吐きだします。

f:id:gsmcustomeffects:20190222022350p:plain
コードの出力

以上でクロックの設定終わりです。

実行してみる。

画像に示す点が確認できればうまく動いていることになる。

f:id:gsmcustomeffects:20190222023102p:plain
デバッグ中の様子

Advanced topic

上記の例では面白くないのでタイマー使ってLチカプログラムを動作させるまでやってみる。
PITのライブラリを追加する

f:id:gsmcustomeffects:20190222030140p:plain
PITライブラリの追加

まずはピンツールで設定をする。

f:id:gsmcustomeffects:20190222024331p:plain
GPIO1をルーティング

ピンの細かい設定

f:id:gsmcustomeffects:20190222024852p:plain
GPIOの設定

パッケージの変更

f:id:gsmcustomeffects:20190222024514p:plain
CVL5に変更

終ったらUpdate Projectをクリックして終了

クロックツールでPITクロックを設定
f:id:gsmcustomeffects:20190222032843p:plain
終ったらUpdate Projectをクリックして終了

次にペリフェラルツールでPITの設定していく。

f:id:gsmcustomeffects:20190222025630p:plain
PITの設定

コードを書く

void PIT_1_IRQHANDLER(){
	GPIO_PortToggle(GPIO1, 1u << 9U);
	PIT_ClearStatusFlags(PIT, kPIT_Chnl_0,kPIT_TimerFlag);
}
int main(void) {

  	/* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();
  	/* Init FSL debug console. */
    BOARD_InitDebugConsole();

    PRINTF("Hello World\n");

    /* Force the counter to be placed into memory. */
    volatile static int i = 0 ;
    /* Enter an infinite loop, just incrementing a counter. */
    while(1) {
        i++ ;
    }
    return 0 ;
}

あとはLチカしてたらOK

参考

gsmcustomeffects.hatenablog.com

gsmcustomeffects.hatenablog.com

別のタイマーでもできるのでGPTについて知りたい方はこれ
gsmcustomeffects.hatenablog.com

MCUXpresso SDK Shellの使い方

MCUXpresso SDK Shellはなんぞ?

組み込み用Shellのことです。

f:id:gsmcustomeffects:20190209061021p:plain
Shellの動作例
SDKに最初から組み込まれているので選択するだけで楽に使えますしprintfとのリンクもIDEがやってくれるので便利です。

使い道としては

  • 信号処理のパラメータの変更
  • 内部データのダンプ

などなど、色々登録しておくと便利に使えます。

サンプルを動かしてみよう

まずはサンプルを動かしてどういうものなのか把握する。

f:id:gsmcustomeffects:20190211011145p:plain
サンプルの選択

サンプルを読み込んでビルドしてデバッグまでやってください。
そしてTeratermを開いて以下のように設定する。

f:id:gsmcustomeffects:20190211012501p:plain
シリアルポートの設定

そのあとプログラムを進めるとこのようになります。

f:id:gsmcustomeffects:20190211012438p:plain
コマンドの入力

次にled 1 onと入力してみましょう。
するとボード上のLEDが光ります。

逆にled 1 offと打つと消えます。

コマンドの詳細が知りたい場合helpコマンドで確認することも可能です。

f:id:gsmcustomeffects:20190211012643p:plain
helpコマンドの確認

実際にコマンドを追加してみる。

実際に使うにはコマンドを登録したり関数を登録しないといけません。
そんなわけで次はコマンドの追加をやってみます。
現在公開されてるShellとSDKドキュメントの引数がなんか違うので注意してください

スキームを示すと

  • 関数のプロトタイプ宣言
  • 関数の実装
  • コマンドの定義
  • コマンドの追加

上記の四つをやってあげればいいということになります。

関数のプロトタイプ

関数のプロトタイプはサンプルと同じようにするだけです。
今回は"mycommand"というタスクを作ってみます。

static shell_status_t mycommand(shell_handle_t shellHandle, int32_t argc, char **argv);

タスクの実装

先ほど宣言した関数の中身を実装していきます。
サンプルなのでエラー処理とかは手抜きでやります。

static shell_status_t mycommand(shell_handle_t shellHandle, int32_t argc, char **argv){
	SHELL_Printf("mycommand[%d]\r\n",(int32_t)atoi(argv[1]));
	return kStatus_SHELL_Success;
}

mycommand arg1で受け取ったarg1を数値に変換して返すというやつですね(実際プログラム中で数値として使ってないので変換するだけ無駄ですね。)
これで関数部分はできました。

コマンドの定義

つぎにShellから呼び出す時のコマンドを定義してあげます。
名前は同じように"mycommand"でいいと思います。

コマンドの追加はSHELL_COMMAND_DEFINEというマクロが定義されているのでそれを使うことにします。

SHELL_COMMAND_DEFINE(mycommand,"\r\n\"mycommand arg1\"\r\n",mycommand,1);

SHELL_COMMAND_DEFINE詳細ですがこのようになっています。
f:id:gsmcustomeffects:20190214041323p:plain

shell_command_tという構造体にいろいろセットするためのマクロです。
shell_command_t構造体の中身はこのようになっています。

f:id:gsmcustomeffects:20190214041616p:plain

引数を整理すると

  1. Shellで呼び出すためのコマンド(char)
  2. helpコマンドで出てくる説明(char)
  3. コマンドにより呼ばれる関数(関数ポインタ)
  4. 引数の数(uint8_t)

という感じです。
最後のメンバのlinkはマクロでは0をセットしてるんですがよくわからなかったです。わかる方教えてください。

コマンドの追加

最後にコマンドの追加です。

SHELL_RegisterCommand(s_shellHandle, SHELL_COMMAND(mycommand));

単純に関数ポインタを渡せばいい

以上でコマンド追加は終わり

実際に動作させてみる

サンプルエクスポートでやっているならコンソール出力はUARTになっているはずなのでTaratermでボーレート115200でCOMをオープンする。

f:id:gsmcustomeffects:20190214043806p:plain
追加したコマンドの動作例

こんな感じにできていればOKだ。

あとは体裁整えるなりコマンドを色々実装していくといいだろう。

よくわかってないこと

ファイルがいろいろ分かれててUARTとのリンクがどこでつながれてるかがよくわかんない
IDEが生成するファイル構造だとserial managerってファイルとdebug consoleってファイルがあるのでどことどこがリンクされてるのか?が結構わかりにくい

下手にsemihost有効にすると動かなくなりそうだしprintf,scanfをfloatありにした場合どうなるのか?、UARTの番号変えるのどうしたらいいのか等。
疑問点はいろいろある。

CMSIS DSP メモ1



色々試してたらGCCの場合最適化しないとFPU命令呼んでくれないっぽい
f:id:gsmcustomeffects:20190207231605p:plain

optimize入れるとちゃんと呼ばれてる
f:id:gsmcustomeffects:20190207231530p:plain

i.MX RT1020(CortrexM7 DP FPU)のSQRTしか試してないのであれですが入れとくに越したことはないかと。
尚、消されたくない変数にはvolatileを付けましょう。

以上

Eclipse expression機能のメモ



mallocとかで確保した変数のスコープをする方法

//変数名がsdram_bufferとすると
(*sdram_buffer@200)

@の後の数字で何個表示するか設定できる。

f:id:gsmcustomeffects:20190207045628p:plain

i.MX RT1020でwavファイルを組み込む方法



今回は組み込みでbinファイルだったり、wavファイルを組み込む方法を紹介しようと思います。
RT1020と書いていますがSTM32とかでも同様なことができます。

環境は以下の通り

  • MCUXpresso IDE v10.3.0 [Build 2200] [2018-12-03]
  • SDK v2.5(RT1020 EVK)
  • MIMXRT1020-EVK: i.MX RT1020 Evaluation Kit

やることは

これができると何ができるのか?
エフェクターで言えば

  1. モデルのパラメータ情報(大容量)をもっておける
  2. キャビネットIRを読み込んだりできる
  3. パーカッション音源を保存しておける

近年のエフェクターの場合ノンパラメトリックで情報を持っている場合もあると思うので信号処理のブロック処理データ数によってはかなり大きいデータになる。
あとは近年はやっているIRデータはwavで配られていることも多いのでそれにも使えると思う。

バイナリファイル(wav,bin,...etc)を読み込む方法について

実際にやって行くわけだがバイナリファイルをリンクする方法は

  • c arrayに変換して貼り付ける
  • オブジェクトファイルとして持ってobjcopyで結合する
  • asm命令(incbin)で読み込む

の3つがある。
それぞれ利点があってobjcopyなんかだとオプションでリンクきれるのでデバッグ時には結構楽。
今回行うincbinでは全体ではなく範囲で切り取ったりするのが可能なので一番柔軟な命令かもしれない。

プロジェクト作成

今回は音の再生まで行うのでSAIペリフェラルを用います。
まずはsai_interrupt_transferを読み込みます。
プロジェクト作成やインポートについて知りたい方はこの記事を参考にしてください
gsmcustomeffects.hatenablog.com

サンプルはsai_interrupt_transferを利用

f:id:gsmcustomeffects:20190205032837p:plain
サンプルのインポート

defineマクロの定義

プロジェクトが作成出来たらasmを書いていきたいのだがCファイルに直接は書けないのでインラインasmを利用して埋め込む
それをdefineマクロで一気に書くとこうなる。(ほとんどChanさんのまま

#define	IMPORT_BIN(sect, file, sym , ofs) asm (\
		".section " #sect "\n"                  /* Change section */\
		".balign 4\n"                           /* Word alignment */\
		".global " #sym "\n"                    /* Export the object address to other modules */\
		#sym ":\n"                              /* Define the object label */\
		".incbin \""file"\","#ofs" \n"                /* Import the file */\
		".global _sizeof_" #sym "\n"            /* Export the object size to other modules */\
		".set _sizeof_" #sym ", . - " #sym "\n" /* Define the object size */\
		".balign 4\n"                           /* Word alignment */\
		".section \".text\"\n")                 /* Restore section */

細かい説明はgnu incbinとかarm incbinとかでググると公式ドキュメントが出てくるのでそちらに譲ることとする。

実際の使い方はこうなる

IMPORT_BIN(".rodata","../source_transfer/ss.wav",FooBin,40);
extern const char FooBin[], _sizeof_FooBin[];

ファイルポインタとファイルサイズ(byte)を同時に取得可能なマクロとなっている(Chanさんすごい・・・
ofsに関しては40を設定していて最初の40byteを読み飛ばすための記述。
wavの場合最初の数バイトはファイル情報が入っているのでそれを音声に乗っけないための処置(正しくはヘッダー全部見ないとわかんない
というよりバイナリダンプしてdataチャンク探してそこから先読めばいい

次にwavをインポート

f:id:gsmcustomeffects:20190205052408p:plain
wavの配置はソースと同様のディレクト

次に再生に関する記述を行っていく。
基本的にはサンプルコードが初期化してくれるのであとはオーディオフォーマットの設定をする。

f:id:gsmcustomeffects:20190205040950p:plain
フォーマットの設定

あくまで例だが自分の場合はこうした。
ちなみにハイレゾの場合24bitのデータがパッキングされるのでそのままでは使えないのでインターリーブして0埋めしてuint32_tにキャストしてLRにパッキングしないといけない

次に再生ポインタの変更
元のプログラムではmusic.hからデータを読み出すものポインタの読み込み先がmusic.hのデータ配列になってる。
それをこのように変更する。

f:id:gsmcustomeffects:20190205045021p:plain
データ読み込み先の変更


ここまで来たらあとはコンパイルしてみよう

f:id:gsmcustomeffects:20190205050023p:plain
wavを含むビルド結果

生データを含むため5MBにもなってしまっている。
MIMXRT1020-EVK: i.MX RT1020 Evaluation Kitの場合QuadSPIflashで既存ストレージが8MBあるので問題ないが通常のマイコンではこの手法はあんまし推奨できないかもしれないと感じる。

再生について

このボードの場合初期化の時点でWM8960のヘッドホンアンプのコンフィグもやってくれるのでこのまま実行すればいい。


まとめ

今回は生データを組み込むための手法を紹介し一例としてwavをリンクしてみた。
上記でも触れているが実行ファイル自体が肥大化するのでデバッグの時の書き込み時間がくそ長くなる。そのためコメントアウトなどして対策は必要
改善策としてはSDカードなどのストレージを用意してlittlefsなんかのファイルシステムで読んであげてデコードするとか?
あとwavごとにフォーマットが違うので先頭ヘッダーから情報抜きだしてそれ相応の設定をするコード追記は必要。(それっぽいデコーダーの実装)

実用的に考えると?

MIMXRT10xx全般に言えることで基本的には密結合RAMにコード配置して信号処理を回すことになるんだけどそうなるとセカンダリブートローダーが必要になる。
その場合メインのアプリケーションプロジェクトとブートローダープロジェクトの二つができる。
アプリのほうは最大でも256KB程度(内蔵RAMの最大値に依存)におさめないといけないのでデータ系のリンクをここにするのはナンセンスである(rodataセクションはどちらにせよFlashに行くため)
メインアプリと分けると読み込んだbinなりwavのポインタを取得しないといけないのでなんかしら対策は必要なはず・・・・

スキームとしては以下のような感じなんだけど最後のほうわからん・・

  1. ブート方法がQuadSPI経由だとしてブートローダはXIPで起動する。
  2. ブートローダのコード自体でincbinしてアプリケーションコード部分だけRAM転送してPC書き換えて起動。
  3. incbinで呼んだファイルのポインタを取得する方法がわからん

別にデメリットばかりでもないよ・・・

フォーマットが決まった短いファイルをいっぱい持っておきたい(例えばキャビネットIRの場合)ケースもある。

f:id:gsmcustomeffects:20190205051824p:plain
キャビネットIRの読み込み例

あとはあんまし書き換えないであろうreverb IRとかそういうのもアリですね。