LPCXpressoとLPC800-MAXでマイコンを学ぶ(その5:PLLを使用してクロックアップ!)
LPC812は最大動作クロックは30MHzなので、今回はデフォルトの12MHzを30MHzで動作するように設定を変更してみます。
動作クロックの変更を行うには
前回のエントリのクロック・ジェネレータを参照しながら考えてみます。
目的は「system clock」を30MHzにすることです。さて入力クロックの種類ですが、
- IRC oscillator
- XTALIN, XTALOUT
- CLKIN
- watchdog oscillator
が選択できるようですがクロックジェネレータには制約があります。
watchdog oscillatorは入力周波数が低すぎて使えません。CLKINは回路図上でクロックがそもそも接続されていません。XTALIN, XTALOUTですが回路図で見ると...
実装はされているのですが、ジャンパーで実は接続されていません。したがって使えるのはIRC osciilator(内部発振器)だけとなります。さて12MHzの内部発振器で30MHzを作るにはどうすればよいでしょう。答えはPLL回路で逓倍・分周することで作ることができます。このあたりもユーザーズマニュアルp50に記述があります。
今回はPLLを使ってSystem clock=30MHzを作りたいので、Main clock = 60MHzを作れということです。その場合
- M divider value : 5
- P divider value : 2
というのがあります。これがPLL回路の設定値となります。この表の少し上に色々と記述があり、PLL回路の制約条件が書いてあります。
整理するとこういうことになります。
今、PLL入力 = 12MHz、Main clock = 60MHz、System clock = 30MHzなので、SYSAHBCLKDIV = 2となります。MとPについては
この式から、
- M = 60MHz / 12MHz = 5
- FCCO = 2 × P × 60MHz (156MHz < FCCO < 320MHz)なので、 P = 2
具体的に設定してみる
前述の設定を具体的に設定してみます。system_LPC8xx.cの設定値を変更します。変更するレジスタは
- SYSPLLCTRL : PLLのMとPを設定する
- MAINCLKSEL : メインクロック選択をPLL outに設定する
- SYSAHBCLKDIV : システムクロックをメインクロックから1/2して生成する
#define CLOCK_SETUP 1 #define SYSOSCCTRL_Val 0x00000000 // Reset: 0x000 #define WDTOSCCTRL_Val 0x00000000 // Reset: 0x000 //#define SYSPLLCTRL_Val 0x00000041 // Reset: 0x000 #define SYSPLLCTRL_Val 0x00000024 // Reset: 0x000 M=5, P=2 #define SYSPLLCLKSEL_Val 0x00000000 // Reset: 0x000 //#define MAINCLKSEL_Val 0x00000000 // Reset: 0x000 #define MAINCLKSEL_Val 0x00000003 // Reset: 0x000 PLL select //#define SYSAHBCLKDIV_Val 0x00000001 // Reset: 0x001 #define SYSAHBCLKDIV_Val 0x00000002 // Reset: 0x001 1/2
ただし注意があります。デフォルトではSYSAHBCLKDIV = 1なのでクロック生成を前段から行っていくと、メインクロック60MHzを出力した瞬間、システムクロックも60MHzとなってしまいます。そのため、まず最初にSYSAHBCLKDIV = 2として、あらかじめシステムクロックをメインクロックの1/2に設定しておくほうが安全です。コードは以下のように修正します。
LPC_SYSCON->SYSAHBCLKDIV = SYSAHBCLKDIV_Val; <-- 安全のため、先に1/2しておく LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_Val; /* Select PLL Input */ LPC_SYSCON->SYSPLLCLKUEN = 0x01; /* Update Clock Source */ while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01)); /* Wait Until Updated */ #if ((MAINCLKSEL_Val & 0x03) == 3) /* Main Clock is PLL Out */ LPC_SYSCON->SYSPLLCTRL = SYSPLLCTRL_Val; LPC_SYSCON->PDRUNCFG &= ~(0x1 << 7); /* Power-up SYSPLL */ while (!(LPC_SYSCON->SYSPLLSTAT & 0x01)); /* Wait Until PLL Locked */ #endif #if (((MAINCLKSEL_Val & 0x03) == 2) ) LPC_SYSCON->WDTOSCCTRL = WDTOSCCTRL_Val; LPC_SYSCON->PDRUNCFG &= ~(0x1 << 6); /* Power-up WDT Clock */ for (i = 0; i < 200; i++) __NOP(); #endif LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_Val; /* Select PLL Clock Output */ LPC_SYSCON->MAINCLKUEN = 0x01; /* Update MCLK Clock Source */ while (!(LPC_SYSCON->MAINCLKUEN & 0x01)); /* Wait Until Updated */ // LPC_SYSCON->SYSAHBCLKDIV = SYSAHBCLKDIV_Val; <--- コメントアウト
動作確認をしてみる
動作確認はprintfを使ってSystemCoreClockを表示してみます。
設定通り30MHzになっていることが確認できました。ここでレジスタをIDE上で確認できる方法を紹介します。ブレークした状態で、左のプロジェクトウィンドウから「Peripherals+」のタブを選択し、Peripheralから今の場合「SYSCON」をチェックします。この状態で右下のコンソールウィンドウの「Memory」タブを選択すると各レジスタの現在の値が表示されます。
各レジスタが設定通りになっていることが確認できます。このレジスタの確認はGPIOその他でも使えますし、その場で「Value」の値を変更することもできるのでデバッグにも使えます。
前回と今回はスタートアップルーチンや、動作クロックの設定について調べてみました。普段ArduinoのようなPICマイコンではこのようなことを気にする必要はありません。しかしプロトタイプ以上のことをやったり、製品に組み込むマイコンの選択をコストなどを考えると、組込みマイコンを使用する必要が出てきます。ここでの方法や基本的な考え方はどのマイコンでもほとんど同じなので、基本さえ押さえておけば応用は可能でしょう。