ガレスタさんのDIY日記

電子回路、Web、組み込み、自作エフェクターを語るblog

IMXRT1050 General Purpose Timer (GPT)

General Purpose Timer (GPT)

前回はPITでしたが今回はもう少し機能があるGPTの話です。

言葉でつらつら書くのが得意ではないので概要をまとめてみた

  • 32bit アップカウンタタイマー
  • 入力キャプチャに対応
  • キャプチャはrising edge,falling edgeに対応
  • outputイベントの生成が可能
  • 12bit prescalerを搭載
  • 複数のクロックソースを持つ

こんな感じですかね。

続きを読む

IMXRT1050 Periodic Interrupt Timer : タイマーのチェイン

今回もタイマーの記事です。
PITの基礎に関しては前回のここを読んでいただければいいかと思います。

gsmcustomeffects.hatenablog.com


今回はTimer0とTimer1をチェインさせて長めのインターバルを作っていこうと思います。

When a timer has chain mode enabled, it will only count after the previous timer has expired. So if timer n-1 has counted down to 0, counter n will decrement the value by one. This allows to chain some of the timers together to form a longer timer. The first timer (timer 0) cannot be chained to any other timer.

原文より引用

  • Timer0のカウンタが0になったらTimer1のカウンタを一個下げていく
  • チェイン可能なのは自分の一個下のタイマーのみ(Timer0は不可)

やっていく

前回とあんましやることは変わらずです。
タイマーを一個増やしてホスト側のタイマーでチェインを有効にしてあげるだけです。
ざっと主要コードを示します。

    pit_config_t pitConfig;
    PIT_GetDefaultConfig(&pitConfig);
    PIT_Init(PIT, &pitConfig)
    PIT_SetTimerPeriod(PIT, kPIT_Chnl_1, 49U);//Timer0 100msec*50回分のカウントで割り込み=5sec
    PIT_EnableInterrupts(PIT, kPIT_Chnl_1, kPIT_TimerInterruptEnable);
    PIT_SetTimerChainMode(PIT, kPIT_Chnl_1, 1);//enable = 1
    EnableIRQ(PIT_IRQn);
    PIT_StartTimer(PIT, kPIT_Chnl_1);
    PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, USEC_TO_COUNT(100000U, PIT_SOURCE_CLOCK));
    PIT_StartTimer(PIT, kPIT_Chnl_0);

やってることをまとめると

  1. Timer1のカウンタ設定(ホスト側なので50程度)
  2. Timer1の割り込みを有効
  3. Timer1でチェインを有効
  4. CMSIS_APIにて割り込みを有効
  5. Timer1をスタート
  6. Timer0のカウンタを設定(100msec)
  7. Timer0をスタート

Timer0(100msec)*Timer1(50カウンタ)なので5000msecで割り込みが生成されます。
見れば分かる通りにTimer1のカウンタにかなり余裕があるので1分とか作れると思います。(用途は色々あると思うがEEPROMの自動保存とかにいいかもしれない)

実行例

数秒待って手動ブレークするとちゃんとTimer1がそれっぽい値になっている
f:id:gsmcustomeffects:20180210002247p:plain

f:id:gsmcustomeffects:20180210002258p:plain

すすめてまってると割り込みハンドラにもきちんと入る

f:id:gsmcustomeffects:20180210002354p:plain

まとめ

PITのチェイン機能を試すことができた。
長時間インターバルを構成可能になった

補足

タイマーのチャンネルは

typedef enum _pit_chnl
{
    kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
    kPIT_Chnl_1,      /*!< PIT channel number 1 */
    kPIT_Chnl_2,      /*!< PIT channel number 2 */
    kPIT_Chnl_3,      /*!< PIT channel number 3 */
} pit_chnl_t;

この中から選べる。(チェインは1,2,3のみ有効

IMXRT1050 Periodic Interrupt Timer 基礎編

Twitterなどでリンクハイライト表記になってしまうので今回からIMXって表記に変えました。
RT1050で検索されれば引っかかるので特に問題ないでしょう。

PIT(Periodic Interrupt Timer)

Periodic(周期的な)タイマーです。
Kinetisの頃からあったペリフェラルなのでそちらに慣れている方は問題ないでしょう

まとめると

  • DMA,割り込み生成可能
  • 独立タイマー
  • IO連携機能はなし(単純な内部カウンタのみをもつため理解しやすい)
  • タイマチェイン機能(連結)

その他面白いなとおもったのは動作させたままcounter periodの変更をできる点ですかね
f:id:gsmcustomeffects:20180205223116p:plain

SDKを見た感じPITは0~3の4つ使えるらしい

Tips(簡単な説明)

とりあえずデータシートにはexampleコンフィグが書いてある。

  1. MCR[MDIS]ビットを0にしてモジュールを無効にする。
  2. LDVAL registerを設定する
  3. TCTRL1[TIE]を設定して割り込みを有効
  4. TCTRL1[TEN]を1にしてタイマースタート

LDVALのセット値の決め方だが
リファレンスマニュアルではモジュールクロックが50MHz前提で説明している。
LDVAL trigger = (period / clock period) -1

ClockPeriod = 50MHz = 20nS

Period = 任意msec(5.12msecにした)

これで計算すると255999(0x0003E7FF)になる。

あとはTIE,TENを設定するだけ。
これだとレジスタセット前提になるのでSDKでのやり方を書いてく。

当ブログではまだCCMの説明してないのでクロックの設定は割愛させてください
普通に説明できるんだけどレジスタに0x形式で直接代入する脳筋SDKなので解説しても何も得はない。

SDKでの設定例

  1. PITの構造体の初期化
  2. Clockセット
  3. Init
  4. Period設定
  5. 割り込み設定
  6. タイマースタート

PIT構造体の初期化

pit_config_t pitConfig;

ぶっちゃけメンバが一個しかいないのでこれいるのか説
f:id:gsmcustomeffects:20180205232934p:plain

Clock

クロックはAPIがあるのでそれを使う

    CLOCK_SetMux(kCLOCK_PerclkMux, 1U);
    CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U);

init

Initも簡単

先ほどの構造体にデフォルト値を取得してセット

PIT_GetDefaultConfig(&pitConfig);

InitAPIをコール

PIT_Init(PIT, &pitConfig);

第一引数はPITだがこいつはレジスタのベースアドレスなのでこう書けばOK
モジュール番号はあとで設定する

Period設定

カウンタもAPIがある

PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, USEC_TO_COUNT(5120U, CLOCK_GetFreq(kCLOCK_OscClk)));

baseアドレスとタイマーチャンネルとピリオドの背定をしている。
ここでUSEC_TO_COUNTとあるがただのマクロなので特に難しくない

#define USEC_TO_COUNT(us, clockFreqInHz) (uint64_t)((uint64_t)us * clockFreqInHz / 1000000U)
  • us : タイマーの割り込み周期uSec
  • clockFreqInHz : タイマーのモジュールクロック

普通にこういうマクロがあるので特に計算する必要もなく自動計算されてセットしてくれる。

割り込み設定

PIT_EnableInterrupts(PIT, kPIT_Chnl_0, kPIT_TimerInterruptEnable);
EnableIRQ(PIT_IRQn);

モジュール側の割り込み有効とCMSISAPIでの割り込み有効をしているだけ

タイマースタート

スタートはAPI呼ぶだけ

PIT_StartTimer(PIT, kPIT_Chnl_0);

実行してみる

あとは普通にハンドラの定義してブレークがかかればOKだ

f:id:gsmcustomeffects:20180206000007p:plain

まとめ

PITの使い方を理解した。
かなり単純な仕組みなので初心者が扱うタイマーとしてはかなり良いと思う。

今後はGPTや高機能タイマーもやっていきたい

IMXRT1050 FlexRAMについて その2

FlexRAM関連のサンプルを眺めていて疑問に思ったことがある。
今回はその戯言の塊を書いていこうと思う

マニュアルにのってないよ?

リファレンスマニュアルのレジスタの一覧はこうなっている
f:id:gsmcustomeffects:20180128014036p:plain

サンプルを見るとアロケーション外のアクセスがあった時割り込みを生成しているのとmagicアドレスマッチングで割り込みを生成している。
前者はマニュアル通りだが問題なのが後者だ。

そんなもの設定するレジスタの説明がない(Fuck!
ドライバ内を参照するとこのように存在しているらしい。

f:id:gsmcustomeffects:20180128014328p:plain

そんなもん知らんがなw(Fuck2回目
それに関連したAPIの方みてみるとこんな記述が
f:id:gsmcustomeffects:20180128014500p:plain

ユーザーが指定アドレスをmagic addに設定してそいつにRead/Writeアクセスがあると割り込みを生成できるみたいだ。

もう一個ふぁっくな点が
ステータスレジスタがアロケート外フラグのものしかないことになってるけど
f:id:gsmcustomeffects:20180128015112p:plain

ドライバ側ではこうなっている
f:id:gsmcustomeffects:20180128014849p:plain

ということは0〜3ビットの予約済みビットがこれに該当することになる。


はぁこの記事タイトルのシリーズ難航しそうだな・・・・・・w


ちなみにこの記事1/25時点でのことです
NXP氏〜マニュアル更新たのんます〜

IMX RT1050 FlexRAMについて その1

ここ最近本格的にこのチップでの開発を始めて数ヶ月経った。
こいつはRAMオンリーということもあり初期設定のままではコードが入らなくなって来た。(初期設定ではコード、データ、ヒープスタックがITCMにリンクされるようになってるため)
どうやら色々調べてみるとFlexRAMという機能がありRAM(全体で512KB)のアロケーションが可能であり512KBを32KB単位で割り振りできるみたいだ。
その機能使ってITCMの範囲を広げたいなと感じため今回はそういった昨日の概要や設定方法などをまとめてみた。
これ知見が何かの役立てば嬉しい。


FlexRAM

NXP曰くFlexRAMは高度な構成が可能なフレキシブルRAMモジュールである。
簡単にいえばRAM array + PINMUX群 + 各種RAMコントローラになる。

f:id:gsmcustomeffects:20180120234432p:plain
figure1:FlexRAM概要

使えるメモリは

  • ITCM(Instruction-Tightly Coupled Memory)
  • DTCM(Data- Tightly Coupled Memory)
  • On-Chip-RAM

の3つである。

ITCMは64bitバス、DTCMはdual 32bitバス(メモリインターリーブ)、OCRAMは64bitバスでアクセス可能

インターリーブはここを参照したい。
https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%AA%E3%83%BC%E3%83%96

TCMインターフェイスはCortexM7コアと同じ速度で動作し同期している。一方OCRAMは64-bit AXI bus経由でのアクセスなので1/4程度のパフォーマンスとなってしまう。
それに関してはCacheを利用することで幾らか改善可能である。

i.MX RT1050の場合は512KBメモリアレイがあるのでその領域を以上3つの領域でアローケーション(自由分割)できる。
なおある程度の制約がある(後述

モリーコンフィグ

この機能を使用するためにはまずコンフィグレーションを行う必要がある。
コンフィグレーションには二通りの設定方法がある。

  1. FlexRAM FUSEを利用(デフォルトはこっち)
  2. IOMUXC_GPR_GPR17 registerを利用

以上二つの設定方法を決めるのが
IOMUXC_GPR_PGR16 registerの FLEXRAM_BANK_CFG_SEL bitである。
f:id:gsmcustomeffects:20180121001716p:plain

CFG_SEL description
0 FUSE利用
1 IOMUXレジスタ17を利用

APIとしては

static inline void FLEXRAM_SetAllocateRamSrc(flexram_bank_allocate_src_t src)
{
    IOMUXC_GPR->GPR16 &= ~IOMUXC_GPR_GPR16_FLEXRAM_BANK_CFG_SEL_MASK;
    IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_FLEXRAM_BANK_CFG_SEL(src);
}

flexram_bank_allocate_src_tはこう

typedef enum _flexram_bank_allocate_src
{
    kFLEXRAM_BankAllocateThroughHardwareFuse = 0U, /*!< allocate ram through hardware fuse value */
    kFLEXRAM_BankAllocateThroughBankCfg = 1U,      /*!< allocate ram through FLEXRAM_BANK_CFG */
} flexram_bank_allocate_src_t;


次に実際のコンフィグを説明して行くことにしよう

FUSEを利用した静的コンフィグ

FlexRAMは4bit設定するこのビットはboot前に設定されている必要がある。
FlexRAM関連のFUSEは0x6D0 addressにマッピングされており [16-19]-bitがそれに当たる。

f:id:gsmcustomeffects:20180121002327p:plain

このようにFuseビットを利用した設定では16通りのRAM構成が選べるわけである。(4bit = 2^4 = 16)
初期状態はOCRAM(256KB),ITCM(128KB),DTCM(128KB)となっている。

IOMUXレジスタを利用した動的コンフィグ

FlexRAMバンクはIOMUXレジスタを書き換えることで動的なコンフィグも可能である。この時IOMUXC_GPR_PGR16 registerの FLEXRAM_BANK_CFG_SEL bitが1である必要がある。
いかにレジスタのdescriptionを示そう。

f:id:gsmcustomeffects:20180121002912p:plain

見ればわかるとおりに2bitで個別に役割を決められる。

モリタイプ定義の大きさ

iMXRT1050は合計512KBのRAM arrayを持っており、16個のブロックがあるので32KB単位でコンフィグして行くことになる。
一方OCRAMはBOOTROMになる関係で0KBでコンフィグできない。
ITCM or DTCMは0KBコンフィグ可能

各種メモリコントローラ

FlexRAMはAXIバスとTCMバス二つをもつモジュールである。
それぞれのメモリコントローラとRAM arrayはマルチプレクサを通して接続されている。

TCM Memory Controller(TCM Interface)

TCM InterfaceはCortexM7コアに同期し同じ周波数にて動作する。
ICTMは命令フェッチ、DTCMはデータを置くのに適している。

Read/Write accessには2つの方法がある。

  1. Fast access mode(1 cycle)
  2. Wait access mode(2 cycle)

wait accessは電力消費低減のための機能

設定APIはFLEXRAM_SetTCMWriteAccessModeってのがある。

static inline void FLEXRAM_SetTCMWriteAccessMode(FLEXRAM_Type *base, flexram_tcm_access_mode_t mode)
{
    base->TCM_CTRL &= ~FLEXRAM_TCM_CTRL_TCM_WWAIT_EN_MASK;
    base->TCM_CTRL |= mode;
}

設定にはTCM CRTL Register (TCM_CTRL)をセットする必要がある。

f:id:gsmcustomeffects:20180121033854p:plain
各種ビットと値による機能は以下の通り
f:id:gsmcustomeffects:20180121033926p:plain

On Chip RAM(AXI Interface)

OCRAMメモリは、64ビットシステムAXIバスに接続されたスレーブポートモジュールとして機能する。

読み込みと書き込みは、2つの独立した読み取り/書き込み制御モジュールによって処理される。(2つの処理が同時に来た場合のためのバスアービタをモジュールに含む)

アービタはラウンドロビン方式で動作する。

ラウンドロビン方式はここを参照したい。
第1回 RTOS概論 | トロンフォーラム

READ/WRITEで同様のバンクか異なるバンクのアクセスがあった場合READの方が高優先度になるみたい

FlexRAM power domains

FlexRAM arrayは3つに分割される。

  • PDRET domain.
  • PDRAM0 domain.
  • PDRAM1 domain.

上の図でいうとこの部分

f:id:gsmcustomeffects:20180121181102p:plain

それぞれのパワーモードでの各種RAMの状態は以下の通りです。

System IDLE LowPower IDLE SUSPEND SNVS
ARM Coreの状態 WFI WFI PowerDOWN OFF
FlexRAM(PDRET) ON ON ON OFF
FlexRAM(PDRAM0) ON ON ON/OFF OFF
FlexRAM(PDRAM1) ON ON PowerDOWN OFF

※SNVS:Secure Non-Volatile Storageのこと

All SOC digital logic, analog modules are shut off only except SNVS
domain
32KHz RTC is alive

とあるのでRTC以外全オフみたいな感じですかね。

この辺に関しては結構細かい設定があるのでリファレンスマニュアルのパワー項目の部分を参照すると良いでしょう。
そのほかAN12094というアプリケーションノートに色々書いてあります。

FlexRAM interrupt

FlexRAMはアロケーション範囲外のアクセスがあった場合に割り込みリクエストを出すことができます。

イベントはITCM,DCTM,OCRAMそれぞれに対して生成できる。
設定はInterrupt Enable Register (INT_SIG_EN)にて設定できる。

f:id:gsmcustomeffects:20180121205137p:plain

Interrupt Status Register (INT_STATUS)でstatus情報を得ることができる。

MIMXRT1050-EVKでスイッチ入力を使う手順

今回は今後よく使うであろうスイッチ入力をやって行く。
まずボードにあるタクトスイッチは以下の四つである

SW2 ON/OFFスイッチ
SW3 リセット
SW4 デバッガチップリセット or bootloader mode
SW8 ユーザースイッチ

ここで SW2はON/OFFスイッチになっておりOFF状態で押すと電源がONになる面白い機能
一方ON状態で短く押すと割り込み生成ができるらしいw
あと長押し5秒でOFFできる。(何だこのゲーム機見たいな機能w

おっと脱線しました・・・・・・・

要はSW8(GPIO5-00)を使うってことですね。
場所はチップの左上にあるやつです。

f:id:gsmcustomeffects:20180119214134p:plain
早速やっていこう

使い方

プログラムの冒頭でコンフィグ構造体の設定をしてあげる。

typedef struct _gpio_pin_config
{
    gpio_pin_direction_t  direction;     /*!< Specifies the pin direction. */
    uint8_t outputLogic; /*!< Set a default output logic, which has no use in input */
    gpio_interrupt_mode_t interruptMode; /*!< Specifies the pin interrupt mode, a value of @ref gpio_interrupt_mode_t. */
} gpio_pin_config_t;

構造体が上記のようなメンバなのでこういう感じに設定してあげる。
ボタンはGNDに繋がっていてるのでこうやって設定する。(ピンのプルアップはこのあと説明

    gpio_pin_config_t sw_config = {
        kGPIO_DigitalInput, 0,
        kGPIO_IntFallingEdge,
    };

次にPINMUXの設定をする。
これは手打ちでやるべきでないのでMCUXpressoのピンコンフィグツールで行った方が無難
f:id:gsmcustomeffects:20180119231707p:plain

ピンのプルアップ抵抗値だったりヒステリシスだったり色々柔軟なことができる。(リファレンスマニュアル:Chapter 32
General Purpose Input/Output (GPIO)を参照)
f:id:gsmcustomeffects:20180119234248p:plain
それでコードを吐くとこうなる。

void BOARD_InitPins(void) {
  CLOCK_EnableClock(kCLOCK_Iomuxc);           /* iomuxc clock (iomuxc_clk_enable): 0x03u */
  CLOCK_EnableClock(kCLOCK_IomuxcSnvs);       /* iomuxc_snvs clock (iomuxc_snvs_clk_enable): 0x03u */

  IOMUXC_SetPinMux(
      IOMUXC_GPIO_AD_B0_12_LPUART1_TX,        /* GPIO_AD_B0_12 is configured as LPUART1_TX */
      0U);                                    /* Software Input On Field: Input Path is determined by functionality */
  IOMUXC_SetPinMux(
      IOMUXC_GPIO_AD_B0_13_LPUART1_RX,        /* GPIO_AD_B0_13 is configured as LPUART1_RX */
      0U);                                    /* Software Input On Field: Input Path is determined by functionality */
  IOMUXC_SetPinMux(
      IOMUXC_SNVS_WAKEUP_GPIO5_IO00,          /* WAKEUP is configured as GPIO5_IO00 */
      0U);                                    /* Software Input On Field: Input Path is determined by functionality */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_12_LPUART1_TX,        /* GPIO_AD_B0_12 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_B0_13_LPUART1_RX,        /* GPIO_AD_B0_13 PAD functional properties : */
      0x10B0u);                               /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/6
                                                 Speed Field: medium(100MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Enabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */
}

あとはInit

GPIO_PinInit(GPIO5, 0, &sw_config);//GPIO5の0ピン

ここでやっとconfig構造体を渡す。
同じ設定使いたかったら構造体の使い回しできるし結構いい設計だと思う。
普通に使うならここで終了。GPIO_PinWrite,Readなりを呼んで使える。

割り込みを使う場合

割り込みの場合はIRQAPIで該当ポートを有効にする。

EnableIRQ(GPIO5_Combined_0_15_IRQn);

な感じで設定。
割り込み番号はデータシートかMIMXRT1052.hを参照

そのあとにGPIO_PinInitをコール(GPIO_SetPinInterruptConfigというAPIをInit内で呼んでいる為)

あとはMaskレジスタの設定をして終わり

GPIO_PortEnableInterrupts(GPIO5, 1U << 0U);

スイッチ押したときこのようにブレークが入ればOK
f:id:gsmcustomeffects:20180119233958p:plain