がれすたさんのDIY日記

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

littlevGL シミュレータを使うメモ

littlevGLのシミュレータを使う時の導入メモ
公式の解説だけだと少しはまったのでまとめておくという感じ。
尚littlevGL 6.0を対象に進めていきます。
なんか8月に下書きでかいてて2020年1月21日現在いつの間にか6.1になってましたがマイナーアップデートなのでおそらく手順的な変更点はないはず

littlevGLについて

f:id:gsmcustomeffects:20200123073118p:plain
LittlevGLの公式デモUI(Dark Styleを適用)
MIT licenceで提供されている組み込みGUIライブラリである。
組み込み向けGUIは商用含め

  • emWin(NXPだとフリー)
  • TouchGFX(STだとフリー)
  • Embedded Wizard(体験版があり)
  • uGFX(趣味ならフリー)

などがあるがlittlevGLはどのプラットフォームでも無料で使えるので覚えておいて損はない。



littlevGLシミュレータを試す

littlevGLについては前項でふれた通りだが、毎度マイコンに書き込んでデバッグしているのではかなり大変である。
そこで活躍するのがPCシミュレータという機能今回はこれの導入方法の紹介

f:id:gsmcustomeffects:20190827032041p:plain





Eclipse CDTの導入

以下のリンクからEclipseCDTを落としてくる。
CDT Downloads | The Eclipse Foundation

インストールまでは普通に進めてください

MinGWの導入

EclipseC言語を扱うにはコンパイラが必要なのでそれを導入します。
ここで注意が必要なのが普通に検索してトップに出てくるMinGWは32bit版なので64bit版を探してきて使います。
MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net


導入に関してはこのサイトが詳しいです。
PENGUINITIS - MinGW 64 bit 版 のセットアップ

パッケージに関して今後管理する必要があるならMSYS2とか入れとくと便利でしょう。
インストールが終わるとC:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\binとかにインストールされてると思うので
このディレクトリにPATHを通してこの段階までは各自進めてください
f:id:gsmcustomeffects:20190827033153p:plain

MinGW PATHなんかでググるとやり方は無限に出てきます。

pc_simulator_sdl_eclipseをクローンする

公式がコンフィグ済みのプロジェクトを配ってくれているのでこれをgit cloneします。
github.com

README読めば書いてありますがgit clone --recursiveとすることが重要です。

これをしないとサブモジュール(lvgl,lv_drivers,lv_drivers)がないのでエラーとなりますので注意してください。

SDL2を持ってくる

SDL2はクロスプラットフォームのマルチメディアライブラリでシミュレータのグラフィック描画に必要。

Simple DirectMedia Layer - SDL version 2.0.10 (stable)

上記リンクに飛んでDevelopmentをダウンロードして適当なところに展開する。
f:id:gsmcustomeffects:20190827034118p:plain

公式ドキュメント通りにやるならこの手順通りにファイルをコピーしたりする。
f:id:gsmcustomeffects:20190827034218p:plain

自分はMinGW直下を汚したくなかったので
C:\SDL2\x86_64-w64-mingw32みたいな感じに配置してEclipseの機能でライブラリパスを通した。(この方法に関しては次の項で説明)

pc_simulator_sdl_eclipseをインポート

先ほど落としてきたpc_simulator_sdl_eclipseEclipseにインポートする。
この状態でいくつかエラーが出ると思うのでプロジェクトのプロパティを開きます。

C++/Build -> ToolChainEditerを開きMinGWを選択

f:id:gsmcustomeffects:20190827040928p:plain

この時正しくPATHが通せていればこうなる
f:id:gsmcustomeffects:20190827041159p:plain

これでApplyを押すとコンパイラ関連のエラーは消える。

次にSDLを読み込む
f:id:gsmcustomeffects:20190827041338p:plain

ライブラリも同様に読み込む
f:id:gsmcustomeffects:20190827041436p:plain

これである程度の修正はOK
あとはlv_conf_template.hをlv_conf.hにコピーする。(これはアップデートが入るたび必要みたい
f:id:gsmcustomeffects:20200121044500p:plain

ここで一度コンパイルを行う。
fb関連でエラーが出るようなら以下のように対応する
f:id:gsmcustomeffects:20200121045007p:plain

起動

あとはRunするとこのような画面が出るはず
f:id:gsmcustomeffects:20200121045025p:plain

Tips

その他のデモを動かしたいならこのようにする
f:id:gsmcustomeffects:20200121045351p:plain

f:id:gsmcustomeffects:20200121053129p:plain

テーマ系を動かすならlv_confのテーマに関する部分を1にしておく必要がある。
f:id:gsmcustomeffects:20200121052341p:plain
ダークテーマはこんな感じ
f:id:gsmcustomeffects:20200121052249g:plain

これでシミュレータの導入はできたと思いますのでご自分でやってみたい方は公式のドキュメントに沿って進めてもらえるといいと思います。
細かいカスタムの仕方および組み込み機器への導入方法はまた今度書きます。

参考文献

公式ドキュメント
docs.littlevgl.com

MIMXRT10xxのeFlexPWMを使ってPWM信号を出力する。

今回はどのマイコンでもよく使う機能の一つでもあるPWMについてやっていく。
MIMX系のマイコンではeFlexPWMというペリフェラルがそれにあたる。

特徴を解説したいところですが長すぎたのでRMのFeatures部分を貼っておくので読みたい人は該当部分を読んでください

f:id:gsmcustomeffects:20191215055621p:plain


テスト環境と留意事項

  • MCUXpresso IDE 11.1(2020,1/1時点で最新
  • MCUXpresso SDK 2.70(2020,1/1時点で最新)
  • MIMXRT1020-EVK(ここは使う環境によって変わる)

今回はNXP公式ボードのMIMXRT1020-EVKを使うことにする。
最近MCUXprressoSDKとIDEが最新版になりePWMのコンフィグツールが入ったので最新版を前提に進めていこうと思います。

eFlexPWM

モジュール自体の内部構造はこのようになっている。

f:id:gsmcustomeffects:20191215055940p:plain
PWM Submodule Block Diagram

ごく一般的な比較レジスタとカウンタの組み合わせ

クロックがIPG経由でもらえるのでかなり高速なPWMを生成可能
16bit分解能というスイッチング電源つくってくれと言わんばかりの機能。

生成できるPWMのタイプ以下の4つ

  • Center Aligned PWMs
  • Edge Aligned PWMs
  • Phase Shifted PWMs
  • Double Switching PWMs

f:id:gsmcustomeffects:20191215062935p:plain
PWM type

PWM 生成のながれ

f:id:gsmcustomeffects:20200104024216p:plain
PWM Generation Block Diagram(クリックで拡大)

eFlexPWMは主に上記のブロックで構成されている。
各機能を含めブロック詳細をこれから解説していく。

PWM Clocking

f:id:gsmcustomeffects:20200104025733p:plain
PWM Clocking(クリックで拡大)

モジュールのクロックはIPBUS_CLK(IPGクロックのこと) , EXT_CLK(外部クロック) , AUX_CLK(submodule0からのクロックを供給)の3つのクロックから選べる。
最後のAUX_CLKはsubmodule0をマスターとしてsubmodule1 , submodule2 , submodule3にクロックを共有できる機能。(submodule0は必ずIPBUS_CLK , EXT_CLKのどちらかから駆動する必要がある。

eFlexPWM Clock Blockの機能としては , Prescalerを内蔵しているので1~128で分周を設定できる。
ここで設定しないといけないのはクロックソースとPrescalerの2つであり、それにより16bit counterのクロック速度が決定する。

Register Reload Logic

f:id:gsmcustomeffects:20200104030858p:plain
Register Reload Logic(クリックで拡大)

eFlexPWM関連のレジスタはダブルバッファで構成されている。(動作中に片側は書き換え可能ということ
Register Reload Logic Blockは、ダブルバッファのリロードのタイミングを設定することができる。

まずMaster ReloadとLocal Reloadだが、Masterはsubmodule0から他のsubmodule1-3を同期してリロードしたい場合に使用する。
ややこしいので例を示すと

submodule0はLocal Reloadを使用
submodule1-3はMaster Reloadを設定

submodule0からのリロード信号でsubmodule1-3のリロードを実行という感じです。

次にLDOK(Load OK )についてだがMaster Control Register (MCTRL)LDOKビットのことでLoadOKのこと。
こいつがセットされるとダブルバッファレジスタのセットされた値を有効にする。
コンフィグツールとSDK使うならユーザーがケアする必要がないので詳しく知りたい方はRM(Reference Manual)のMaster Control Register (MCTRL)の項を読んでください。

Mod Compareは、Control Register (SM0CTRL - SM3CTRL)COMPMODEのことでこれもSDKのコンフィグツールで設定可能なのでユーザーはno careでいい

Half CompareはリロードをPWMのハーフでするかの設定。
Control Register (SM0CTRL - SM3CTRL)のHALFビットで設定可能。同様にしてFULLビットも設定可能。
これもSDKのコンフィグツールで設定可能なのでユーザーはno careでいい

Timer synchronization

f:id:gsmcustomeffects:20200104043708p:plain
Submodule Timer Synchronization(クリックで拡大)

Timer(counter) synchronizationはカウンタの初期化同期の機能を提供する。
LocalSync , Master Reload , Master Sync , PWM_EXT_Syncの4つから選べる。

LocalSyncはそのモジュール自体でPWM周波数などを制御する。
Master Reloadはsubmodule0のReload頻度に他のモジュールがロックされる。
Master Syncはsubmodule0のLocalSync信号がそのまま自身のcounter Init挙動になる。
PWM_EXT_Syncこれはチップ外の信号にcounter Init挙動を任せる。

FORCE_OUTのFRCENビットがセットされている場合は上記のどれがセットされていようがFORCE_OUT信号のアサート時にカウンターを初期化できる。
この機能はRM曰く

When PWM signals are commutated on an inverter controlling a brushless DC motor, it is necessary to restart the PWM cycle at the beginning of the commutation interval.
This action effectively resynchronizes the PWM waveform to the commutation timing.

とあるように転流タイミングでのPWM同期をとるためっぽい。

PWM Generation

f:id:gsmcustomeffects:20200104064228p:plain
PWM Generation(kurikkudekakudai )

VALn Register とコンパレータでH/L信号を作りPWMを生成する部分
このセクションもSDKコンフィグツールで

  • Center Aligned PWMs
  • Edge Aligned PWMs
  • Phase Shifted PWMs
  • Double Switching PWMs

選択することで勝手に設定されるのでユーザーはno careでいい
ちなみにDuty CycleとかPWM周波数もコンフィグで入力できる部分があるので楽に設定可能

Force Out Logic

f:id:gsmcustomeffects:20200104065733p:plain
Force Out Logic(クリックで拡大)

このモジュールは、直訳するなら強制出力。
ForceOut信号自体は8つから選択出来てその信号が有効になるとあらかじめ設定された出力に強制的に切り替わる
その時出力できるのが、PWM23(positive,negative)、OUT23、PWM_EXTAの4つ

OUT23はSoftware Controlled Output Register (SWCOUT)で設定でき常時1か0を出力する。

Deadtime Insertion Logic

f:id:gsmcustomeffects:20200104072900p:plain
Deadtime Insertion Logic(クリックで拡大)

ハーフブリッジとかフルブリッジとかで同時ON(ショート)を防ぐための機能
SDKコンフィグツールで時間入力する部分があるのでレジスタとかはno careでいい

Fault Protection

f:id:gsmcustomeffects:20200104074713p:plain
Fault Protection(クリックで拡大)

Fault保護機能のこと。
下のほうはデバッグ中PWM止めるか止めないかを選べる機能できちんと設定しておかないと勝手にPWMが止まることになる。

Faultピン(Fault0-3)の接続先だがこの辺の資料に外部回路の例が載っているのでまじめにやるならばACMP(Analog Comparator)とFaultピンをXBARでリンクするなりの工夫が必要。
それ以外はXBARの内部接続機能でHIGH,LOWどっちか固定を選べる。

https://www.ti.com/jp/lit/an/jaja502a/jaja502a.pdf

https://www.analog.com/media/jp/technical-documentation/application-notes/jan105.pdf

http://www.ti.com/jp/lit/ml/jajy091/jajy091.pdf

Output Logic

f:id:gsmcustomeffects:20200104233735p:plain
Output Logic(クリックで拡大)
PWMの極性設定ができる。

実際にやっていく

ここまででPWMモジュールのブロック図の中身をあらかた解説したので次はMCUXpressoSDK内臓のペリフェラルコンフィグツールを使ってPWM出力を行う。
まず新規プロジェクト作成でpwm , xbaraをインポートしてプロジェクトを作成する。

f:id:gsmcustomeffects:20200105003608p:plain
新規プロジェクト作成

クロックの設定

f:id:gsmcustomeffects:20200105005003p:plain
クロックの設定
クロックのルートはコアクロック付近からもらってきているのでだいぶ高速である。
そのため自分に合ったところに落とすために周辺にある分周器に印をつけておいた。

ピンの設定

f:id:gsmcustomeffects:20200105020516p:plain
ピンの設定

PWM1を使うこととして、引き出せるピンの都合上、submodule3を使うことにする

f:id:gsmcustomeffects:20200105021334p:plain
使用するピン

PWMペリフェラルの設定

MCUXpressoSDK ペリフェラルコンフィグツールでPWM1を選択して画面を開く

設定値はあくまで参考程度だと思ってください

f:id:gsmcustomeffects:20200105035003p:plain
peripheral設定(クリックで拡大)
f:id:gsmcustomeffects:20200105035941p:plain
peripheral設定(クリックで拡大)
f:id:gsmcustomeffects:20200105040427p:plain
peripheral設定(クリックで拡大)

メインコード

一応動作済みのコードを示す。

Duty変更時のディレイはSystickタイマーで作っています。
PWM_SetPwmLdok(PWM1_PERIPHERAL, (kPWM_Control_Module_3), true);
PWM_StartTimer(PWM1_PERIPHERAL, (kPWM_Control_Module_3));

の部分はコンフィグツールのバグでsubmodule0のコードしか吐いてくれないのでやむなくmainでLoadOKとタイマーセットをしてる感じです。

ループではPWM_UpdatePwmDutycycleでDutyを更新してLDOKを読んでレジスタをReloadしているだけなので簡単ですね。

#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MIMXRT1021.h"
#include "fsl_debug_console.h"
#include "fsl_xbara.h"
/* TODO: insert other include files here. */

/* TODO: insert other definitions and declarations here. */

/*
 * @brief   Application entry point.
 */

uint32_t delayCount;

void SysTick_Handler(void)
{
    if(delayCount != 0x00)
    {
    	delayCount--;
    }
}

void Systick_delay(uint32_t ms){
	SysTick->LOAD  = (SystemCoreClock/1000 & SysTick_LOAD_RELOAD_Msk) - 1;
	delayCount = ms;
	while(delayCount!=0x00);
}
int main(void) {

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


    PRINTF("Hello World\n");
	SysTick_Config(SystemCoreClock/1000-1);
    PWM_SetPwmLdok(PWM1_PERIPHERAL, (kPWM_Control_Module_3), true);
    PWM_StartTimer(PWM1_PERIPHERAL, (kPWM_Control_Module_3));
    while(1) {
        for (int i = 0;  i <=90;  i=i+10) {
        	PWM_UpdatePwmDutycycle(PWM1_PERIPHERAL, kPWM_Module_3, kPWM_PwmA, kPWM_SignedCenterAligned, i);
        	PWM_SetPwmLdok(PWM1_PERIPHERAL, (kPWM_Control_Module_3), true);
        	Systick_delay(2000);
		}
        __asm volatile ("nop");
    }
    return 0 ;
}

動作時の波形はこのような感じです。

f:id:gsmcustomeffects:20200105041223p:plain
PWMの波形
f:id:gsmcustomeffects:20200105042636p:plain
PWMの波形

LEDの点灯密度もきれいに取れたので貼っておきます

f:id:gsmcustomeffects:20200105055900p:plain
PWMでLED点灯をしている図

PWM周波数が20kHz となっているので正しく波形が出ていることが確認できる。

参考資料

i.MX RT1020 Processor Reference Manual

MIMXRT10xxのADC External Trigger Controlを使ってみる(software trigger編)

概要

今回はMIMXRT10xxのExternal Trigger Controlについてやっていくことにする。

ADC External Trigger Control(以下ADCETC)は下図に示すようにADCモジュールと他のペリフェラルをつなげてコアの介在なしにデータ転送(DMA使用)したりトリガ変換したりできる便利なやつです。
f:id:gsmcustomeffects:20191128013713p:plain

Ref Manualによれば

The ADC_ETC module enables multiple users share the ADC modules in a TIMEDivision-Multiplexing (TDM) way.
The external triggers can be from Cross BAR(XBAR) and TSC in SOC.
The ADC_ETC has two channels, each channel can support one TSC and four external triggers from XBAR to one ADC module.
The ADC_ETC can support interrupt mode and DMA mode.

まとめるとTDM方式で他のモジュールがADCモジュールを使用可能でありTouch Screen Controller (TSC)とCross BAR(XBAR)からトリガ可能である。
割り込みとDMAをサポートしているということ。

通常のADCモジュールではCPU-ADC間でデータのやり取りをするがADC ETCを利用するとXBARでつながっているペリフェラルとADCを連動できる。
その他ADCチェインという機能も使えて複数ピンを読んだりするとき設定しておくといい感じに捌いてくれる。

テスト環境と留意事項

  • MCUXpresso IDE 11.0.1
  • MCUXpresso SDK 2.50(ここは使う環境によって変わる)
  • MIMXRT1020-EVK(ここは使う環境によって変わる)

今回はNXP公式ボードのMIMXRT1020-EVKを使うことにする。

今回の内容は前回のADCの記事を読んでADCモジュール単体での使い方を理解している人向けなので注意してください
またペリフェラルツールを多用するのでその辺の使い方がわからない人はブログの一番下の参考文献に貼ってある記事を読んでください

software trigger

まずはsoftware triggerからやっていく。
ADC_ETC software triggerはpolling APIで使用するのと何が違うのか?

polling APIの場合はADCモジュールのコンフィグを行い変換完了後レジスタを読みに行く。
複数チャンネル読むには本数分その動作を行わなければならない。

一方でADC_ETCのソフトウエアトリガ機能を使うとソフト上からハードトリガ信号を出してコンバージョンを開始する。
加えてチェイン機能というものがあり、あらかじめ登録したチェインに従って連続で変換を行ってくれる。
指定チャンネルの変換が終わるとそれぞれ別々のレジスタから値を持ってくることができ毎度毎度データレジスタをケアしなくていい。

ここでADCモジュールに対してはハードトリガであること、ソフト側はETCをかえしてADCを操作していることことに注意。

f:id:gsmcustomeffects:20191210084314p:plain

次の項から新規プロジェクトからADC_ETCを使用する流れを説明していく。

プロジェクトの作成

通常通りプロジェクトを作成します。
ADC,ADC_ETCをインポートを忘れずに行ってください
f:id:gsmcustomeffects:20191212002109p:plain

ピンの設定

まずはGPIOのオルタネート設定でこのピンをADCとして使うというのを設定してあげます。

ピン設定ツールを開いてADCのピンを設定する。
今回はIN13,IN12,IN11,IN10を使うのでそれを設定する。
f:id:gsmcustomeffects:20191211010548p:plain

評価ボードでいうArduinoヘッダピンを使っている。
f:id:gsmcustomeffects:20191211011549p:plain

ADCの設定

次にADCモジュールの設定をしていく

f:id:gsmcustomeffects:20191211021039p:plain
ADCの設定例(画像が小さくて見れない方はクリックして拡大

ADC_ETCの設定

次にADC_ETC側の設定をしていく

f:id:gsmcustomeffects:20191211025625p:plain
ADC_ETCの設定例(画像が小さくて見れない方はクリックして拡大

設定が終わったら上部のupdate codeをクリックしてコードをジェネレートしてください。
そうするとperipherals.cにADC、ADC_ETCの初期化コードが出力されるはずです。

ここで出力コードにバグがあるので一部修正します。

/*
const adc_etc_trigger_chain_config_t ADC_ETC_1_TC_0_chain_config[4] = {
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 0,
    .ADCChannelSelect = 13,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 1,
    .ADCChannelSelect = 12,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 2,
    .ADCChannelSelect = 11,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 3,
    .ADCChannelSelect = 10,
    .InterruptEnable = kADC_ETC_Done0InterruptEnable
  }
};
*/

const adc_etc_trigger_chain_config_t ADC_ETC_1_TC_0_chain_config[4] = {
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 1U<<0,
    .ADCChannelSelect = 13,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 1U<<1,
    .ADCChannelSelect = 12,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 1U<<2,
    .ADCChannelSelect = 11,
    .InterruptEnable = kADC_ETC_InterruptDisable
  },
  {
    .enableB2BMode = true,
    .ADCHCRegisterSelect = 1U<<3,
    .ADCChannelSelect = 10,
    .InterruptEnable = kADC_ETC_Done0InterruptEnable
  }
};

修正箇所はADCHCRegisterSelect メンバへの代入部分です。

SDK自体のADCHCRegisterSelect メンバの中まで行くとコメントアウトで解説があります。
f:id:gsmcustomeffects:20191212035334p:plain

要は10進じゃなくビットフィールドで管理しろということなので、10進のまま入れるなら2U,4U,8U...な感じで代入しないといけないということですね。

RMマニュアルをよく読んでフィールドが8ビットであることtriggerが8本あることが頭に入っていないと気付きにくいバグですね。
(むしろユーザーはSDK側で対応されてると思ってしまうでこういうの本当にやめてほしい)

f:id:gsmcustomeffects:20191212035603p:plain
ETC_TRIG Chain 0/1 Register

それとフィールドの説明more informationと書かないでここでしてくれ・・・・・

ユーザー実装コード

最後にユーザーが実装しないといけないコード部分を貼っておく
やってることを箇条書きで示しておく

  • GETCHARして何か入力されるまでwait
  • ADC_ETC_DoSoftwareTriggerをコールしてADCのチェインコンバージョンを開始(0,1,2,3の4つのADC
  • 最後のADCが終わったらADC_ETC_IRQ0_IRQHandlerに飛んでETCの該当データレジスタからデータを取り出す。
  • 割り込みフラグの解除
#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MIMXRT1021.h"
#include "fsl_debug_console.h"
/* TODO: insert other include files here. */

/* TODO: insert other definitions and declarations here. */

/*
 * @brief   Application entry point.
 */
volatile uint32_t g_adc_value[4]={0};
volatile bool g_AdcConversionDoneFlag;

void ADC_ETC_IRQ0_IRQHandler(void)
{

    g_AdcConversionDoneFlag = true;
    g_adc_value[0] = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 0U); /* Get trigger0 chain0 result. */
    g_adc_value[1] = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 1U); /* Get trigger0 chain1 result. */
    g_adc_value[2] = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 2U); /* Get trigger0 chain2 result. */
    g_adc_value[3] = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 3U); /* Get trigger0 chain3 result. */
    ADC_ETC_ClearInterruptStatusFlags(ADC_ETC, kADC_ETC_Trg0TriggerSource, kADC_ETC_Done0StatusFlagMask);
    __DSB();
}


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. */
    /* Enter an infinite loop, just incrementing a counter. */
    while(1) {
    	g_AdcConversionDoneFlag = false;
		PRINTF("Press any key to get user channel's ADC value.\r\n");
		GETCHAR();
		ADC_ETC_DoSoftwareTrigger(ADC_ETC, 0U); /* Do software XBAR trigger0. */

		while (!g_AdcConversionDoneFlag)
		{
		}
		PRINTF("ADC conversion value is %d,%d,%d,%d\r\n", g_adc_value[0],g_adc_value[1],g_adc_value[2],g_adc_value[3]);
	}
}

PRINTFに関してはUARTとリンクしてもいいしsemihostとリンクしてもいいと思う。
NXP実装のGETCHARは微妙に調子が悪いのでメインの待ち部分はSystickタイマーとかPITとかGPT使うといい感じに連続コンバージョンできると思う。

まとめ

ADC、ADC_ETCを使うことでETCソフトトリガーで複数チャンネルを変換しPRINTFすることができた。
この例ではトリガー1本のみ使用、ADC1のみの使用なので凝った例ではないがNXP提供のサンプルを含めADC関連の理解に貢献出来たらうれしいと思う。

f:id:gsmcustomeffects:20191212040919p:plain
MCUXpressoIDEの変数グラフ表示機能でADCをみている図

MEMO

途中で出てきたインターバルについて解説してなかったので
f:id:gsmcustomeffects:20191212041700p:plain

応用的なやつ

f:id:gsmcustomeffects:20191212042309p:plain

Seeed Arch Mixで始めるi.MX RT1052(2)

前回は基礎的なことをやりましたが初回の手間が多かったように感じます。

  • コンフィグの際メモリをQSPIに変更
  • メモリサイズの変更
  • XIPのコード編集

毎度新規プロジェクトを立てるたびにこれをやるのは非常に手間です。
そういうわけで今回はこのようにSDKプレビューでSeeedArchMixのメニューを作ってしまおうという話

f:id:gsmcustomeffects:20191124042113p:plain

imageの編集

まずはメインに表示される画像を変更する
docs/imagesに画像ファイルを追加する。

f:id:gsmcustomeffects:20191124052804p:plain

EVKB-IMXRT1050_manifest_v3_5.xmlを開く

EVKB-IMXRT1050.gifで検索してそこを自分の置き換えたい画像ファイル名に変える。
f:id:gsmcustomeffects:20191124053002p:plain

自分が変えた限りでは2か所ほどあったような気がする。

メモリサイズの変更

評価ボードではHyperflashの64Mが載っているがそれをQSPIの8Mに変更する。
同じようにEVKB-IMXRT1050_manifest_v3_5.xmlファイルを開く
CTRL+Fでcfxって検索するとHYPERFLASHのcfxが引っかかるのでそこを
MIMXRT1050_SFDP_QSPI.cfxに変更する。

CTRL+FでExtFlashと検索
その下のサイズを00800000に変更
f:id:gsmcustomeffects:20191124053443p:plain

最後に二行目のid ,nameを変更します。

<ksdk:manifest xmlns:ksdk="http://nxp.com/ksdk/2.0/ksdk_manifest_v3.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="SDK_2.x_EVKB-IMXRT1050_SeeedArchMix" name="SeeedArchMix" brief="This is SDK version 2.6.1 manifest file. It describes the content of the EVKB-IMXRT1050 and additional settings for tools that support SDK version 2.6.1" format_version="3.5" api_version="2.0.0" configuration="ce80e9d0836fd28a5cecddd6116414c1" xsi:schemaLocation="http://nxp.com/ksdk/2.0/ksdk_manifest_v3.0.xsd http://nxp.com/mcuxpresso/sdk/sdk_manifest_v3.5.xsd">

該当部分をSeeedArchMixとしておけばいいでしょう。
id部分だけは先頭部分を消すとマニフェストがエラーとなるのでSDK_2.x_EVKB-IMXRT1050_SeeedArchMixとでもしておくのが無難でしょう。

XIPコードの編集

/*
 * Copyright 2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "evkbimxrt1050_flexspi_nor_config.h"

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.xip_board"
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/
#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__)
__attribute__((section(".boot_hdr.conf")))
#elif defined(__ICCARM__)
#pragma location = ".boot_hdr.conf"
#endif

//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_LoopbackFromDqsPad,
            .csHoldTime = 3u,
            .csSetupTime = 3u,
            // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock
            .sflashPadType = kSerialFlash_4Pads,
            .serialClkFreq = kFlexSpiSerialClk_100MHz,
            .sflashA1Size = 8u * 1024u * 1024u,
            .lookupTable =
                {
                    // Read LUTs
                    FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
                    FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),
                },
        },
    .pageSize = 256u,
    .sectorSize = 4u * 1024u,
    .blockSize = 256u * 1024u,
    .isUniformBlockSize = false,
};
#endif /* XIP_BOOT_HEADER_ENABLE */

まとめ

以上の変更を適用して再度SDKを読み込む
f:id:gsmcustomeffects:20191124053744p:plain

このようにメモリも初期値がArchMix用になっている
f:id:gsmcustomeffects:20191124053838p:plain

最後にXIPのコードも
f:id:gsmcustomeffects:20191124053916p:plain

以上でSeeedArchMixを変更なしで始めれるようになった。

尚SDKExampleのほうは画像は変わるがほかは元のままなので注意。
(ExampleのほうはそのフォルダごとにXMLもってるのでそれを編集すればいけるが面倒)

Seeed Arch Mixで始めるi.MX RT1052(1)

今回はSeeedStudioが出しているArchMixを使ってi.MX RT1052入門をやっていく

f:id:gsmcustomeffects:20191115210420p:plain
Seeed Arch Mix

上記の写真にあるようにかなりコンパクトに収まっている。
また値段も$29.90であり半端ない。
USB、SD、SDRAMも使えるのでお安くi.MX RTに触れたい人にとってもおすすめである。

というわけで今回はこれを使って基礎的な部分を学ぶ
環境としてはMCUXpressoIDEを使うのでそれをインストールしておいてください。
またMCUXpressoSDKでIMXRT1050用のものをビルドしてダウンロードしてください。

f:id:gsmcustomeffects:20191115225713p:plain

ダウンロードしたらIDESDKを読み込んでください
やり方はこの記事見ればわかると思います。
MCUXpresso IDEのペリフェラルコンフィグツールを使ってみる - がれすたさんのDIY日記

はじめに起動周りを整理しよう

このマイコンは内部メモリがないので外部flashから起動するXIPという方式をとることになる。*1
外部メモリとしてはHyperFlash、QSPIFlash、SDなどがあるがこのボードについているのはQSPI Flashなので評価ボードと同じプログラムでは動かない*2
修正部分を解説するためにまずは新規プロジェクトを作成する。

ドライバの部分は何も変更しなくていいので次に進む。
メモリの設定は以下の画像を参考に変更する。
f:id:gsmcustomeffects:20191115220117p:plain

そうするとプロジェクトが出来上がるのでxipというフォルダの中身を見ていく
f:id:gsmcustomeffects:20191116003851p:plain

Hyperflash向けの設定が書かれているのでこういう風に編集します。

/*
 * Copyright 2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "evkbimxrt1050_flexspi_nor_config.h"

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.xip_board"
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/
#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__)
__attribute__((section(".boot_hdr.conf")))
#elif defined(__ICCARM__)
#pragma location = ".boot_hdr.conf"
#endif

//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_LoopbackFromDqsPad,
            .csHoldTime = 3u,
            .csSetupTime = 3u,
            // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock
            .sflashPadType = kSerialFlash_4Pads,
            .serialClkFreq = kFlexSpiSerialClk_100MHz,
            .sflashA1Size = 8u * 1024u * 1024u,
            .lookupTable =
                {
                    // Read LUTs
                    FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
                    FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),
                },
        },
    .pageSize = 256u,
    .sectorSize = 4u * 1024u,
    .blockSize = 256u * 1024u,
    .isUniformBlockSize = false,
};
#endif /* XIP_BOOT_HEADER_ENABLE */

この状態でとりあえずビルドしてデバッグしてブレークポイントがちゃんと機能することを確認する。
デバッガ接続は以下のピンを使う
f:id:gsmcustomeffects:20191116220732p:plain

うまくできていたらこのようにブレークで止まる
f:id:gsmcustomeffects:20191116220521p:plain

この記事も合わせて読んでくれると自作ボードを作るときに参考になるかもです。
MIMXRT1052CVL5Bを使う際のTips - がれすたさんのDIY日記

というわけで初めからウエイト重めでしたが初期構成は終わりです。

まずはLチカからやってみる。

というわけで先ほどのプロジェクトでLチカが動くようにしましょう。
ArchMixには3色LEDが載ってるのでそれを光らせていきます。

f:id:gsmcustomeffects:20191116222604p:plain

実際にはピン設定ツールを起動して方向などの設定
f:id:gsmcustomeffects:20191116222626p:plain

全部の設定が終わったらコードを出力する。

最後にメインを以下のプログラムに書き換えればRGBのLEDが点滅するはず。

/*
 * Copyright 2016-2019 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of NXP Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
/**
 * @file    MIMXRT1052xxxxB_Project.c
 * @brief   Application entry point.
 */
#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MIMXRT1052.h"
#include "fsl_debug_console.h"
/* TODO: insert other include files here. */

/* TODO: insert other definitions and declarations here. */

/*
 * @brief   Application entry point.
 */

uint32_t delayCount;

void SysTick_Handler(void)
{
    if(delayCount != 0x00)
    {
    	delayCount--;
    }
}

void Systick_delay(uint32_t ms){
	SysTick->LOAD  = (SystemCoreClock/1000 & SysTick_LOAD_RELOAD_Msk) - 1;
	delayCount = ms;
	    while(delayCount!=0x00);
}

int main(void) {

  	/* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();
  	/* Init FSL debug console. */
    BOARD_InitDebugConsole();
    PRINTF("Hello World\n");
    SysTick_Config(SystemCoreClock/1000-1);
    /* Force the counter to be placed into memory. */
    volatile static int i = 0 ;
    /* Enter an infinite loop, just incrementing a counter. */
    while(1) {
        GPIO_PinWrite(GPIO1, 9, 0);
        Systick_delay(200);
        GPIO_PinWrite(GPIO1, 9, 1);
        Systick_delay(200);
        GPIO_PinWrite(GPIO1, 10, 0);
  Systick_delay(200);
	GPIO_PinWrite(GPIO1, 10, 1);
	Systick_delay(200);
        GPIO_PinWrite(GPIO1, 11, 0);
	Systick_delay(200);
	GPIO_PinWrite(GPIO1, 11, 1);
	Systick_delay(200);
        /* 'Dummy' NOP to allow source level single stepping of
            tight while() loop */
        __asm volatile ("nop");
    }
    return 0 ;
}

LチカのためのタイマーはSystickタイマーを使っています。*3

まとめ

Seeed Arch Mixを用いてデバッグ環境を構築してLチカをやった。

IMXRT関連の記事はここにまとめてあるので興味があれば読んでください
i.MXRT1050 カテゴリーの記事一覧 - がれすたさんのDIY日記

*1:速度面で不利だと感じたら二段ブートローダでRAM展開して実行なんてこともできる。

*2:公式評価ボードはHyperFlashがついてる

*3:ARM共通のシステムタイマー搭載されてない機種もあるのでデータシート参照

MCUXpressoSDK USB Device StackでMIDIStreamingSubClassを使う2

前回はディスクリプタを編集して認識まで行った。
今回はMIDIの送受信が可能となったのでそれの紹介

f:id:gsmcustomeffects:20191110125826p:plain

上に示すのがevkbimxrt1050_dev_audio_generator_bmのsourceフォルダの中身。
枠で囲ってある部分が今回やる部分

ソフトウエアはevkbimxrt1050_dev_audio_generator_bmサンプルを改変する形で実装してるのでこのサンプルをインポートしておいてください。
ハードウエアははForlinx Embedded Tech. Co., Ltd.のOK1052-Cで検証を行っています。

尚、細かいところは未検証なのでかろうじて動くサンプルだと思って読んでください
特に送信や受信はBusy判断とかはしてないので連続で送るとエンドポイント側のAPIがBusy返すと思います。

usb_device_midi.c/.h , usb_device_class.c

もともとあったusb/device/class/audio/usb_device_audio.c/.hファイルを削除し同じディレクトリにusb_audio_midi.c/.hを作成してください
ファイルを新しく作成していますが元のAUDIOクラスのコードを引用して持ってきてcontrol subclassとstreaming subclassの記述を消してmidistreamingに関する記述に置き換えただけです。
具体的にはエンドポイントの初期化が主になるのでBulk endpointの初期化とBulkIn/Blukoutコールバックの追加など

該当ファイルを以下のコード参考に記述してください

usb_device_midi.c


usb_device_midi.h


usb_device_class.c

以下のコードを冒頭部分のマクロに追記してください

#if ((defined(USB_DEVICE_CONFIG_AUDIO_MIDI)) && (USB_DEVICE_CONFIG_AUDIO_MIDI > 0U))
#include "usb_device_midi.h"
#endif



#if ((defined USB_DEVICE_CONFIG_AUDIO_MIDI) && (USB_DEVICE_CONFIG_AUDIO_MIDI > 0U))
    {USB_DeviceAudioMIDIStreamingInit, USB_DeviceAudioMIDIStreamingDeinit, USB_DeviceAudioMIDIStreamingEvent, kUSB_DeviceClassTypeAudio},
#endif

full code here

https://gist.github.com/GSMCustomEffects/fd5ef3465c426e067ff88be18ad72015


audio_generator.c , usb_device_config.h

audio_generator.c

メインであるこのファイルはほとんど変えていないのですが変更箇所が結構飛び飛びなので全部貼っておきます。

まあkUSB_DeviceEventSetConfiguration部分が変わるのとUSB_DeviceAudioCallbackに自身の処理を加えただけです。


usb_device_config.h

ここでどのdevice classを使うのかコンフィグできる。

元のUSB_DEVICE_CONFIG_AUDIO を無効にしてUSB_DEVICE_CONFIG_AUDIO_MIDI を新規定義して有効化する。

/*! @brief Audio instance count */
#define USB_DEVICE_CONFIG_AUDIO (0U)

#define USB_DEVICE_CONFIG_AUDIO_MIDI (1U)

usb_device_descriptor.c

最後にディスクリプタの一部変わっているので以下の内容に書き換えてください

変更内容としてはusb_device_audio.hを呼ぶところをusb_device_midi.hに変更
関連してエラーの出るところを修正(主にマクロでusb_device_audio.hより引用してる部分

動作確認

以下のページよりpocket MIDIをダウンロードしてください
https://www.morson.jp/pocketmidi-webpage/

上部のinput outputよりポートを選ぶ
上部メニューのViewよりProgramChangeを開いて何か送る
このように両側にMIDIが表示されればOK

f:id:gsmcustomeffects:20191110154502p:plain

動かすとこんな感じ

所感

ある程度手抜きだがUSB MIDIの送受信が動くようになった。
今後は新規プロジェクトからコンフィグツールを使ってUSB周りが動くような記事を書く予定

参考文献

公式ドキュメントは必ず読む

  • MCUXpresso SDK USB Stack User’s Guide.pdf
  • MCUXpresso SDK USB Stack Device Reference Manual.pdf

USB StackのアプリケーションノートはないのでLPC5500用のアプリケーションノートを読む

AN12458 はリクエスト周りのドキュメントとしてはかなり分かりやすい。

  • AN12458 USB to Virtual COM on LPC54018 and LPC5500
  • AN10420 USB virtual COM port on LPC214x

USB 2.0 Specificationは公式ドキュメントなので必須

USB Device MIDIの公式資料


USB Complete: The Developer's Guide
規格は説明が硬すぎるのでかみ砕いた説明が欲しい方向け