前回の記事の続きです。
RXマイコンでの時間計測の仕組みを説明していきます。
↓ソースコード
GitHub - hubnoki/RXtacho: Designing tachometer using RX220
時間計測処理
RXマイコンのタイマモジュールを使って時間計測をする仕組みを用意しています。これを少し説明します。
マイコンボード回路
まず、今回使用しているRX220マイコンボードには、20MHz、32.768kHzの水晶発振子が載っています。
RXマイコンとの接続は、
- XTAL(pin7), EXTAL(pin9)に20MHz水晶発振子 (メインクロック)
- XCIN(pin4), XCOUT(pin5)に32.768kHz水晶発振子 (サブクロック)
現状、20MHzのほうだけ使用しています。
水晶発振子の型番は特に記載はありませんが、秋月での取り扱いを見ると、50ppmの精度のものかと思います。一般的にも100ppm(=0.01%)程度の精度はあるものかと。精度がこれだけあれば十分です。
表面実装型クリスタル(水晶発振子) 20MHz (4個入): パーツ一般 秋月電子通商-電子部品・ネット通販
RXマイコン機能 -クロック構成
今回使用しているRX220の内部構成は以下のようになっています。
参照 : RX220グループ ユーザーズマニュアル ハードウェア編
以下からダウンロード可能です。
先ほどの水晶発振子はこの中の「クロック発生回路」に接続され、マイコンのシステムクロックを生成します。
さらに「クロック発生回路」の内部構成。
- 水晶発振子はまずそれぞれ発振回路に接続され、クロック信号を生成します。
- 2つのクロック信号、およびマイコンが内部で持つクロック(高速オンチップオシレータ、低速オンチップオシレータ)の中から、どの信号を大元のクロックとして使用するか、セレクタで選択します。
- 分周器に入力されます。1/1、1/2、1/4、… 1/64といった7種類の分周クロックが生成されます。
- FlashIF用クロック、システムクロック(ICLK)、周辺モジュールクロック(PCLKB、PCLKD)それぞれでセレクタがあり、どの分周クロックを使用するか選択します。
ソースコードのhardware_ilb.cのrx220_init_setup()
関数でこのレジスタ設定手順が記述されています。
このコードに至る前に、e2studioで生成されたiodefine.hがインクルードされており、この中にレジスタ定義が記載されています。
Renesas純正のコンパイラと環境を使っていればこのあたりのドライバを生成してもらえますが、今回はgccを使用しているので、自分で作る必要があります。
void rx220_init_setup() { int i; ////// Clock settings ////// SYSTEM.SCKCR.BIT.PCKD = 0x00; // Peripheral clock D divider SYSTEM.SCKCR.BIT.PCKB = 0x00; // Peripheral clock B divider SYSTEM.SCKCR.BIT.BCK = 0x00; // External bus clock divider SYSTEM.SCKCR.BIT.ICK = 0x00; // System clock divider SYSTEM.SCKCR.BIT.FCK = 0x00; // Flash IF clock divider //// Register protect off //// SYSTEM.PRCR.WORD = 0xA503; //// Main clock //// SYSTEM.MOSCCR.BIT.MOSTP = 0x1; // Main oscillator stop SYSTEM.MOFCR.BIT.MODRV = 0x0; // SYSTEM.MOFCR.BIT.MODRV2 = 0x3; // SYSTEM.MOFCR.BIT.MOSEL = 0; // // P36 : EXTAL, P37 : XTAL Port PORT3.PDR.BIT.B6 = 0; PORT3.PDR.BIT.B7 = 0; SYSTEM.MOSCWTCR.BIT.MSTS = 0xC; // Main oscillator wait cycle, 0xC : 65536 cycles SYSTEM.MOSCCR.BIT.MOSTP = 0x0; // Main oscillator start while(SYSTEM.MOSCCR.BIT.MOSTP != 0x0) ; // wait for oscillator to be stable // SYSTEM.SCKCR3.BIT.CKSEL = 0x2; // Switch clock source, 3 bits, 010 : main oscillator SYSTEM.SCKCR3.WORD = 0x200; // SCKCR3 ... bit access was unavailable //// HOCO stop //// SYSTEM.HOCOCR.BIT.HCSTP = 1; SYSTEM.HOCOPCR.BIT.HOCOPCNT = 1; //// LPC //// SYSTEM.MSTPCRA.BIT.ACSE = 0; // All module clock stop mode enable -> disable SYSTEM.MSTPCRA.BIT.MSTPA15 = 0; // CMT unit0 (CMT0, CMT1) enable SYSTEM.MSTPCRB.BIT.MSTPB30 = 0; // SCI1 enable SYSTEM.MSTPCRB.BIT.MSTPB26 = 0; // SCI5 enable SYSTEM.MSTPCRB.BIT.MSTPB25 = 0; // SCI6 enable SYSTEM.MSTPCRB.BIT.MSTPB4 = 0; // SCI12 enable //// RTC //// #if USE_RTC == 1 //使っていないので割愛 #endif //// Register protect on //// SYSTEM.PRCR.WORD = 0xA500; ... まだ続く ...
- システムクロックコントロールレジスタ(SCKCR)で、FlashIF、システムクロック等のセレクタの設定を行います。すべて1/1(分周なし)を選択しています。
- クロック発生回路のレジスタ設定の前に、プロテクトレジスタ(PRCR)で書き込みを許可します。
- MOSCCR、MOFCR、MOSCWTCRといったレジスタを使用して、メインクロック発振回路を停止→設定→動作開始→安定待ちという手順を踏みます。なお、この間CKSEL(1段目のセレクタ)の設定はデフォルトの0(LOCO選択)となっており、LOCO(低速オンチップオシレータ)で動作します。
- メインクロックの発振が安定したら、CKSELを2に設定して、クロックソースをメインクロックに切り替えます。
- 最後に、PRCRレジスタで書き込みを禁止します。
- 途中にちょっと別の処理(CMTモジュールイネーブル、SCIモジュールイネーブル)も入ってしまいましたが、ご容赦。
これ以降も、rx220_init_setup()
関数では周辺モジュールの初期設定等が続きます。
RXマイコン機能 - CMT0モジュール
RX220には、CMT(Compare Match Timer)モジュールが2つ用意されています。
CMTモジュール1つには16ビットカウンタが2チャネルあります。
今回は、このモジュールで1msごとに割り込みを発生させ、ms単位の時間測定ができるようにしています。
上のrx220_init_setup()
関数の続きで、以下のように設定しています。
//// 1ms timer (CMT0) setting //// CMT0.CMCR.BIT.CKS = 0x0; // count source : PCLK / 8 CMT0.CMCR.BIT.CMIE = 1; // interrupt enable CMT0.CMCOR = 2499; // interrupt cycle : PCLK(=20MHz) / 8 / 2499 = 1ms CMT.CMSTR0.BIT.STR0 = 1; // CMT0 count start
割り込みルーチンはgenerate/inthandler.cの以下の部分で登録しています。ここで呼んでいる関数については次で説明しますが、これがCMTモジュールの割り込み周期(=1ms)ごとに呼ばれます。
#include "timer_soft.h" // CMT0 CMI0 void INT_Excep_CMT0_CMI0(void) { timer_soft_int(); }
ソフト処理
割り込みが発生するごとに、カウンタ変数をカウントアップしていき、これで時間を刻みます。
経過時間取得の手順は以下の通りです。
- リセット関数を呼ぶ(現在のカウンタ変数値を取得)
- カウント数読み出し関数を呼び、リセットからのカウント数を得る
src/timer_soft.cおよびtimer_soft.hにこのあたりを記述しています。
まずカウンタ変数。timer_soft.c内のstatic変数になっています。
static unsigned long t;
タイマのリセット関数とカウント値取得関数です。timer_soft_reset
関数で現在のカウンタ変数値を保存します。ここの引数のtm
を使って、後でtimer_soft_count
関数でリセット実施からのカウント数を取得します。
static unsigned long get_counter();// tm : timer variable // tm is reset void timer_soft_reset(unsigned long tm) { tm = get_counter(); }
// tm : timer variable // return : count from resetting tm unsigned long timer_soft_count(unsigned long tm) { return get_counter() - tm; }
上記に出てくるget_counter
関数は、単にカウンタ変数値を返す関数です。このソースコードを8bitマイコンで使用したことがあり、そのときは単純にはできなかったので、関数化しています。
コメントアウト部にそのコードが残っていますが、割愛します。
前に出てきた、割り込み時に呼ばれるtimer_soft_int
関数では、カウンタ変数を無条件でインクリメントしています。0xFFFFFFFFまで行くと、次は0に戻ります。32bitのフリーランカウンタとなります。
static unsigned long get_counter() { return t; }
ここがちょっとしたポイントで、32bitフリーランカウンタにしておくことで、リセット関数を呼んだ時のカウンタ変数値にかかわらず、単純な引き算だけで最大カウント数の時間計測 (1[ms] * 232 = 4294967.296 [s] ~= 1200[h]) を行うことができます。
このやり方は学生のときのバイトで教わりました。
// increment counter // must be called by interrupt routine void timer_soft_int() { t++; }
次回に続く
今回はここまでにします。
次回は、加速度ログに加速度センサからの割り込み間隔測定値を追加していきたいと思います。
その他
同じRX220ボードを使用されているサイトがあったので、参考でリンクを貼っておきます。