LPC810のMRT
マルチレートタイマ(MRT)について学習
クロック定義の確認
system_lpc8xx.hの
#define CLOCK_SETUP 1 #define SYSOSCCTRL_Val 0x00000000 // Reset: 0x000 #define WDTOSCCTRL_Val 0x00000000 // Reset: 0x000 #define SYSPLLCTRL_Val 0x00000041 // Reset: 0x000 #define SYSPLLCLKSEL_Val 0x00000000 // Reset: 0x000 #define MAINCLKSEL_Val 0x00000000 // Reset: 0x000 #define SYSAHBCLKDIV_Val 0x00000001 // Reset: 0x001
をイジれば、クロック設定ができるようだ。~_Valにある名前がそのままレジスタ名に対応し、値もそのままレジスタ値である。
LPC81xユーザーマニュアルのクロック系統図でどのクロックがmainclock/systemclockに使われるかが分かりやすく図示されている。
セレクタのところには対応するレジスタ名(~SEL)が指示されてるので、これに合わせてレジスタ値を選択すれば、望みのクロックにできるだろう。
とりあえず一番下のCLKDIVの値を大きくしてLEDの点滅が遅くなったのは確認した。
なお、CLOCK_SETUPを0にするとクロック定義は無効になる。つまりリセット後のデフォルトの状態で動作することになる。
PLLを使ってクロック周波数を変えたい場合は、以下の式を満たすようにMSEL/PSELレジスタを設定する。
Fclkout = M x Fclkin = (FCCO) / (2xP)
なんか分かりづらい感じではあるが、まずは入力に対して何逓倍にしたいか?で素直にM値を決めて良いと思われる。
そして「M逓倍後の周波数 x 2P」の値が、156MHz から 320MHzの範囲に入るようなPを設定すればよい。
例えば、Fclkinが12MHzで、これを2逓倍にしたのなら、M=2とする。
この時、156MHz < 24MHz x 2P < 320MHzを満たすP値にすればよい。(P=4でOK?)
もちろんデバイスの最大動作周波数を超えた設定で動作させてはいけない。(暴走する?)
MRT
ドライバのlpc8xxx_mrt.cのソースを手前で用意したLED点滅プロジェクトに実装。
以下はmainでinit_mrt(int32_t xxx)を呼んだ時の呼びだし先のソース
/* Enable clock to MRT and reset the MRT peripheral */ LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10); // ← multi rate timerにクロックを供給 10bit目を1set LPC_SYSCON->PRESETCTRL &= ~(0x1<<7); // ← multi rate timerにリセットアサート 7bit目を0set LPC_SYSCON->PRESETCTRL |= (0x1<<7); // ← multi rate timerにリセット解除 7bit目を1set (負論理) mrt_counter = 0; // ← 32bit integer LPC_MRT->Channel[0].INTVAL = TimerInterval; // ← ダウンカウンタtimerにロードする値 LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31; // ← MSBビットはINTVAL.IVALUE値が書き換えられたときにtimerに対して // どのようにするかを設定する。 // 1の場合はカウント途中でも強制的にロードする。 // 0の場合は、timerが0になったときにロードする。 LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA; // ↑ MODEは0になっていた。INTENは1になっていた。 // MODE=0は、割り込みモードを繰り返す。INTEN=1は割り込みONの設定。
・MRTの初期化
・引数によるタイマー値設定 … ダウンカウンタで0になると割り込み発生し、割り込み先でmrt_counterを+1する。
割り込みで呼ばれた先のソース
void MRT_IRQHandler(void) { if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) { LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */ // ↑ 1の書き込みでクリアされる仕様 mrt_counter++; // ← mainで参照するmrt_counterをカウントアップ } return; }
MRT_STAT_IRQ_FLAG は1に定義されている。
STAT.INTFLAGが1になっていたら、タイマーがダウンカウントで0になって割り込み発生、状態保留中を意味する。
と、だいたい分かったところで、MRTに供給されているのがsystem clockだとしてこれを12MHz、
さらにINTVAL.IVALUE=0x8000(32768)の設定の時、2秒間隔でLEDがON/OFFするようなmrt_count値を算出。
2s / ( 1/12MHz * 32768 ) = 732 ↑ mrt_countのカウントアップ時間
// Enter an infinite loop, just incrementing a counter while(1) { i++ ; if (mrt_counter==732) { LPC_GPIO_PORT->CLR0 = 1 << 2; // PIO0_2をLowに設定(LED点灯) // for(l=0;l<0xFFFF;l++); } else if (mrt_counter==1464) { LPC_GPIO_PORT->SET0 = 1 << 2; // PIO0_2をHighに設定(LED消灯) // for(l=0;l<0xFFFF;l++); mrt_counter=0; } }
これで動作確認したら、想定どおり約2秒間隔でLEDが点滅した。
ただし時計による目視確認なので、いずれオシロスコープでON/OFF波形確認したいところ。