タイマーの学習
R8C/M12Aには3つのタイマー RJ2、RB2、RC があります。多くの目的に使えるよう複雑な機能を持つようですが、情報が不足しているために使い方がわかりません。
数少ない情報から実験的に学び得たものを書いていこうと思います。
1 タイマーRB2 クロックをカウントして一定時間ごとに割り込みをかける
2 タイマーRC レジスタの値をセットするとPWM出力が得られる機能
3 タイマーRJ2 外部パルスをカウントする機能
1 タイマーRB2 2015.06.01
クロックをカウントして一定時間ごとに割り込みをかける
最初にこの働きだけが理解できたので書きます。独断のために間違いがあるかもしれません。
/***********************************************************************/
/* */
/* FILE :timer_rb2.c */
/* DATE :Sun, May 31, 2015 */
/* DESCRIPTION :main program file. */
/* CPU GROUP :M12A */
/* */
/* This file is generated by Renesas Project Generator (Ver.4.19). */
/* NOTE:THIS IS A TYPICAL EXAMPLE. */
/***********************************************************************/
#include "sfr_r8m12a.h"
#define EI() asm("fset i") //割り込み許可
#define DI() asm("FCLR I") //割り込み禁止
void main(void);
void main(void)
{
/******** クロックを高速オンチップオシレータ(20MHz)に変更 ****************/
prc0 = 1; // プロテクト解除
hocoe = 1; // 高速オンチップオシレータ発信開始
{int i; for(i=0; i<50; i++ );} // 安定するまで少し待つ(約10ms)
hscksel = 1; // 高速オンチップオシレータのクロックに切換え
scksel = 1; // システム基準クロック 低速→高速切換え
prc0 = 0; // プロテクト保護
#if 1
/***********************************************************************
タイマRBの設定 1msごとに割り込みを発生させる (SFR)の初期化
20MHzが(x-1)*(y-1)に分周、カウントされて割り込みが発生する */
msttrb = 0; // タイマRB2を有効にする(MSTCRのb1)
trbmr = 0x00; // 動作モード、分周比設定
trbpre = 200-1; // プリスケーラレジスタ
trbpr = 100-1; // プライマリレジスタ
trbir = 0x80; // タイマ割込み要求
ilvlc = 0x03; // 割込み優先レベル設定 停止=0
trbcr = 0x01; // カウント開始
/**** end タイマRBの設定**************************************************/
#endif
#if 0
/***********************************************************************
タイマRBの設定 1msごとに割り込みを発生させる (SFR)の初期化
20MHzが16ビットでカウントされて割り込みが発生する */
msttrb = 0;
tcnt16_trbmr = 1; // trbmr = 4 と同じ16ビット =0 8ビット
// trbmr = 4; // trbmr = 4 と同じ16ビット =0 8ビット
trbpre = 0x20;
trbpr = 0x4e; // 4e(78)*256+20(32)=20000で分周
trbif_trbir = 0; // フラグクリア
trbie_trbir = 1; // bit7=1 割り込み許可
ilvlc = 0x01; // 割込み優先レベル設定 停止=0
tstart_trbcr = 1; // タイマースタート
/**** end タイマRBの設定**************************************************/
#endif
/********** port 初期化 **************************************************/
pd1_0=1; // p1_0 out put
p1_0=0; // p1_0=low
/********** end port 初期化 ***********************************************/
EI(); //割り込み許可
}
/********* end main **************************/
/************************************************************************/
/* タイマRB 割り込み処理 */
#pragma interrupt intTRB(vect=24)
void intTRB( void )
{
trbif_trbir = 0; // フラグのクリア
p1_0 = ~p1_0; // p1_0反転
}
/***** end タイマRB 割り込み処理 *****************************************/
クロックは20MHzでテストしています。
2つの設定がありますが、異なるソースを参考にしたので表記の仕方も変わっています。
いずれも結果的にクロックを20,000カウントして1msごとに割り込みを発生させ、p1_0に接続したledを1msごとにON,OFFしています。
システムクロックをカウントして時間を測る方法に2通りがあって、上のものは8ビットのプリスケーラーで分周(200分周)したものを8ビットカウンタで100カウントして時間を得ています。
下のものは、2つの8ビットレジスタを1つの16ビットカウンタとして使っています。
trbpre が下位8ビット、trbpr が上位8ビットになります。
どちらにするかを決めるのが trbmr レジスタの bit2(bit名tcnt16)で、0はプリスケーラ付きの8ビットカウンタ、1が16ビットカウンタになります。
関係レジスタのビット値(0,1)の設定は レジスタ名=8ビット値 と書く方法と、ビット名[アンダースコア]レジスタ名=0(or1)と書く方法があるようです。
また、msttrb = 0 はレジスタmstcrのbit1ですが、ビット名だけで指定できるようです。これに0を書くとアクティブとなり、1を書くとスタンバイ(カウントしない)になるようです。
2つを比較すると tstart_trbcr ビットは trbcr レジスタのbit0になることがわかります。
関係するレジスタは多くありますが、上の目的のために必要な物はこれだけのようです(あとはデフォルトでOK)。
なお、割り込みを使うかどうかにかかわらず、設定時間になれば フラッグ trbif_trbir が 1 になるので、カウントを続けるにはクリア(=0)せねばなりません。
また、設定と実効を関数にして、フラッグが立つまで空ループをまわり時間が経過すればリターンするというdelayルーチンもありました。
1 タイマーRB2 おわり
2 タイマーRC
レジスタの値をセットするとPWM出力が得られる機能
タイマーRCには16ビットカウンターがあって、0〜カウンターセット値のカウントを繰り返し、
0に戻った時に対応ポートがHになり、レジスタセット値になればポートがLになる機能があります。
カウンタの上限値(セット値)とレジスタの値を適当に決めるとPWMが得られます。
http://www.mcr.gr.jp/tech/r8cm12a/main009.htmlのサイトから学びました。コピーを実行したところ10Hz以下のゆっくりした速度でLEDが激しくちらつきます。よく見ると、設定は20MHzなのですが、プログラムは低速クロックのままになっていました。20MHzの高速クロックに変更するとLEDの明るさが滑らかに変わるようになりました。
理解できた範囲を書きます。
1 3個のポートに(同じ周期ですが)PWM出力を出すことができます。
(IOB) p1_2 p1_4 p1_6 (IOC)p1_3 p3_4 (IOD)p1_0 p3_5 p3_7 3グループからそれぞれ1つを選ぶことができます。
2 PWMの周期はカウンタ設定値(16bit)×クロック周期です。
クロックに20MHz、カウンタに20000-1を設定すると周期は1msになります。
3 パルス幅を変更するには B C D のレジスタの値を変えますが、パルスのon,offに影響しない時点でする必要があります。
プログラム中の void set_trciob( int out )にあります。(精査していません)
レジスタの設定が面倒ですが、プログラムの次の部分は役に立つと思います。
/* ポートの入出力設定 */
pd1 = 0x7f; /* ポート1の入出力設定 */
pd3 = 0xff; /* ポート3の入出力設定 */
pd4 = 0xff; /* ポート4の入出力設定 */
pda = 0xff; /* ポートAの入出力設定 */
/* タイマRCによるPWM出力(3ch出力) */
msttrc = 0; /* タイマRCを有効にする */
p12sel1 = 0; /* TRCIOB端子の選択 */
p12sel0 = 1; /* P1_2をTRCIOB端子にする */
// p14sel2 = 1; /* TRCIOB端子の選択 */
// p14sel1 = 0; /* P1_4をTRCIOB端子にする */
// p14sel0 = 0;
// p16sel1 = 1; /* TRCIOB端子の選択 */
// p16sel0 = 1; /* P1_6をTRCIOB端子にする */
p13sel1 = 0; /* TRCIOC端子の選択 */
p13sel0 = 1; /* P1_3をTRCIOC端子にする */
// p34sel1 = 0; /* TRCIOC端子の選択 */
// p34sel1 = 1; /* P3_4をTRCIOC端子にする */
p10sel1 = 0; /* TRCIOD端子の選択 */
p10sel0 = 1; /* P1_0をTRCIOD端子にする */
// p35sel1 = 0; /* TRCIOD端子の選択 */
// p35sel0 = 1; /* P3_5をTRCIOD端子にする */
// p37sel1 = 1; /* TRCIOD端子の選択 */
// p37sel0 = 1; /* P3_7をTRCIOD端子にする */
pwm2_trcmr = 1; /* PWMモード */
pwmd_trcmr = 1; /* TRCIOD出力 PWM出力する */
pwmc_trcmr = 1; /* TRCIOC出力 PWM出力する */
pwmb_trcmr = 1; /* TRCIOB出力 PWM出力する */
trccr1 = 0x80; /* カウントソース = f1 */
trcgra = 20000-1; /* PWM周期の設定 1/20M*20000=1ms*/
trcgrb = 0; /* TRCIOB端子のON幅の設定 */
trcgrc = 0; /* TRCIOC端子のON幅の設定 */
trcgrd = 0; /* TRCIOD端子のON幅の設定 */
ed_trcoer = 0; /* TRCIODの出力 出力許可 */
ec_trcoer = 0; /* TRCIOCの出力 出力許可 */
eb_trcoer = 0; /* TRCIOBの出力 出力許可 */
cts_trcmr = 1; /* TRCCNT カウント開始 */
このプログラムのでは、p1_7 をAD変換入力として使いVRの中点に接続しています。このポートを0〜5Vに変化させ、そのカウント値をBレジスタにセットすることでVRの回転角に応じてp1_2のLEDの明るさが変わります。
ハードウエアの接続は、
p1_0 LED
p1_2 LED
p1_3 LED
p1_7 (入力)VR中点(0〜5V)
プログラム
2 タイマーRC おわり
3 タイマーRJ2 外部パルスをカウントする機能
2015.07.07
タイマーRJ2には外部パルスをカウントする機能があります。周波数カウンタの実験をするために調べました。16ビットのダウンカウンタです。
パルス入力は TRJIO端子 です。p4_6、p1_7、p1_5端子にTRJIO機能を割り付けられます。
カウント時間は別のところでタイマーなどによって、
tstart_trjcr = 1; でカウントを始め、 tstart_trjcr = 0; でストップします。
16ビットのオーバーフローは フラグ trjif_trjir == 1 で判断できます。判断の後 trjif_trjir = 0 にします。
途中ですが、なぜかタイマーでは ビット名 だけではエラーとなり、ビット名_レジスタ名 を書かねばなりません。
パルスのカウントルーチンを次に書きます。このサイトから引用しました。
long freq_measurement(void){ // カウント数を返す関数です
long freq;
//
freq = 0; // カウント数初期設定
msttrj = 0; //TRJ2マスタービット 0=activ 1=standby
//p46sel0 = 1; //p4_6 → input TRJIO に設定
//p46sel1 = 0; //同上
//p46sel2 = 1; //同上
p17sel0 = 0; //p1_7 → input TRJIO に設定
p17sel1 = 1; // 同上
tmod0_trjmr = 0; //ivent count mode 2,1,0ビットを010にする
tmod1_trjmr = 1; //--
tmod2_trjmr = 0; //--
tstop_trjcr = 1; //counter stop 値はffffhになる
tstop_trjcr = 0; //counter stop clear ストップしない
tundf_trjcr = 0; //under flag 0=non
trjif_trjir = 0; //オーバーフロー要求フラグ0=Without request flag 1=request flag, with well
flg = 1;
//
while (flg != 0) {
if (trjif_trjir == 1) { //request flag, with well
trjif_trjir = 0; // down flag
freq++;
}
}
if (trjif_trjir == 1) {
trjif_trjir = 0;
freq++;
}
freq = (freq * 65536) + ~trj;
return (freq);
}
このルーチンに跳び込むとカウンタを初期設定して flg により動作が決まります。
flgは別のタイマールーチンで制御します。
次がRTCの1Hzパルスによる割り込みルーチンです。
void intINT3( void ){
switch (flg) {
case 0:
break;
case 1:
tstart_trjcr = 1;
flg = 2;
break;
case 2:
tstart_trjcr = 0;
flg = 0;
break;
}
}
カウント準備ができている flg=1 なら tstart_trjcr = 1; でカウントを始めます。flg値は影響のない2とします。
flg=2なら、カウント中ですから(そして1秒経っているから) tstart_trjcr = 0; でカウントを止めます。
2015.07.11
この例ではカウンタのアンダーフローをポーリングしていますが、RJ2割り込みを実行すると割り込みで処理できます。
//カウンタの初期設定で割り込みを有効にする
ilvlb = 0x03; // 割り込みレベル 3 or 2=level2 1=lebel1 0=不許可
//割り込み関数を作る
#pragma interrupt intRJ(vect=22)
void intRJ(void){
trjif_trjir = 0;
//(変数のインクリメント);
}
こちらのほうが私の好みです。
3 タイマーRJ2 おわり
1 めんにゅー 1 ほ そ く
1 めんにゅー 1 おわり
工事中