忍者ブログ

Fグループ電子工作講座

秋月電子SH7125ボードで始めるマイコン開発

秋月RX220タイマプログラム解説

「★RX220マイコンのプログラムを作って、動かしてみよう☆」のp.66付近に記載のあるプログラムの解説です。
同プログラムはCDの「動かしてみようSAMPLE\sample\第4章」内のTEST1.cに相当します。

ハードウェアマニュアル

ここから先はハードウェアマニュアルと見比べながら機能を理解する必要があります。
ハードウェアマニュアルとは秋月のCPUボードやベースボードの説明書ではなく、
CPUボードに搭載されているCPU自体のマニュアルを指します。
サンプルプログラム内の「マニュアル参照」もハードウェアマニュアルを指しています。

秋月のページからもダウンロードできますが、バージョンが古く誤記もある様なので
ハードウェアマニュアルはルネサスのサイトからダウンロードします。

とりあえずRX220のページへ行って
https://www.renesas.com/ja-jp/products/microcontrollers-microprocessors/rx/rx200/rx220.html

「ドキュメント」を選択
「ユーザーズマニュアル:ハードウェア」にチェック
検索



出てきた「RX220グループ ユーザーズマニュアル ハードウェア編」をクリック


飛んだ先でもう一回「RX220グループ ユーザーズマニュアル ハードウェア編」をクリック

やっとダウンロードできました。


CMTの原理

コンペアマッチタイマの原理を理解しておきます。
タイマー動作で増えるカウント値と設定値(上限値)比較(コンペア)し条件に一致(マッチ)した時に何かする機能がコンペアマッチタイマーです。
説明はshのPWM設定の回とほぼ同じです。

CPUのクロックをカウントしていき、設定値になったらカウント値を初期値にリセットします。
リセット直後から再びカウントを行い、再び上限値になったらカウント値をリセットします。
RX220の場合カウントが設定値に達したら自動的に0にリセットされるようです。

これがコンペアマッチタイマを使った繰り返し動作の例です。
繰り返しの周期はクロックの周期×上限値となります。
クロックが一定であるため、カウント値の増加時間の経過に比例タイマーにより自動的に増加していることと同じと考えることができます。

図では10カウント目でリセットをかけていますが、実際には数百カウントでリセットをかけます。また純粋なCPUのクロック周期では早すぎるためCPUのクロックがn回動作したら1カウントアップさせるといった方法をとります。クロック2回で1カウントとすると見かけ上の動作速度が2分の1になります。この設定を2分周器と呼びます。

rx220では4種類のクロックが使用可能で、このクロックに対して分周器を設定して使います。

プログラム解説

準備が整ったのでプログラムを見ていきます。
ここから先だらだらと書くので非常にながったらしい説明になります。

RX220でCMTを扱ううえで必要なのは
・CPUクロックの設定
 ↓
・CMT機能の初期化
 ↓
・CMTを利用
の順で実行する必要があります。

サンプルプログラムでは
main関数内(49~68行目)でクロックの設定
timer関数内(31~33行目)でCMTの初期化
timer関数内(34~43行目)でCMTを利用しています


プログラム実行の流れに沿って
main関数→タイマー関数の順に動作をみていきます。


メイン関数部分
void main(void)
{
    SYSTEM.PRCR.WORD = 0xa50b;              //クロックソース選択の保護の解除
    //SYSTEM.OPCCR.BIT.OPCM = 0x02;         //動作モードの設定。低速/中速
    //SYSTEM.SCKCR.LONG = 0x00001412;       //マニュアル参照。

    //SYSTEM.SCKCR.BIT.PCKD = 0x00;         //マニュアル参照。
    SYSTEM.SCKCR.BIT.PCKB = 0x01;           //マニュアル参照。
    //SYSTEM.SCKCR.BIT.BCK = 0x00;          //マニュアル参照。
    SYSTEM.SCKCR.BIT.ICK = 0x00;            //マニュアル参照。
    //SYSTEM.SCKCR.BIT.FCK = 0x00;          //マニュアル参照。

    /*クロックソースの選択*/
    SYSTEM.SCKCR3.WORD = 0x0200;            //大元のクロックにメインクロックを使用する。
    //SYSTEM.SCKCR3.WORD = 0x0300;          //大元のクロックにサブクロックを使用する。

    /*クロックの元栓の設定*/
    SYSTEM.MOSCCR.BYTE = 0;                 //メインクロック発振器動作
    SYSTEM.SOSCCR.BYTE = 0;                 //サブクロック発振器動作
    //SYSTEM.LOCOCR.BYTE = 1;               //LOCO動作
    //SYSTEM.HOCOCR.BYTE = 1;               //HOCO動作
    //SYSTEM.ILOCOCR.BYTE =1;               //IWDT専用オシレータ停止


    PORTH.PODR.BYTE=0x00; 
    PORTH.PDR.BYTE=0xff;
    while(1)
    {   
        PORTH.PODR.BIT.B0=1;
        PORTH.PODR.BIT.B1=0;
        timer(1000);
        PORTH.PODR.BIT.B0=0;
        PORTH.PODR.BIT.B1=1;
        timer(1000);                        //timer(ミリ秒)
    }
}

<用語>
PRCR:プロテクトレジスタ
重要な機能には間違って変更してしまわないように保護がかかっており、
その保護機能を解除・設定します。

OPCCR:動作電力コントロールレジスタ
最大動作速度と消費電力の兼ね合いを設定します。
消費電力が大きいけど高速動作や速度を落として低消費電力などのおおよその設定を行います。
初期設定は中速動作モード1Aになっています。

SCKCR:システムクロックコントロールレジスタ
分周器の設定を行います。

PCKB周辺モジュールクロックB(S12AD以外)の分周器設定
PCKD:周辺モジュールクロックD(S12AD用)の
分周器設定
BCK:外部バスクロック(RX220未対応機能)の分周器設定
ICK:メイン機能クロック(CPU、DMAC、DTC、ROM、RAM)の分周器設定
FCK:FlashIF
クロック(ROM、E2 データフラッシュ)の分周期設定
設定値
 0x00:1分周
 0x01:2分周
 0x02:4分周
 0x03:8分周
 0x04:16分周
 0x05:32分周
 0x06:64分周

SCKCR3:システムクロックコントロールレジスタ3
4つのクロックの大元となるクロックにどれを使用するか設定します。
・外付けメインクロック(20MHz)
・外付けサブクロック(32.768KHz:時計用)
・HOCO CPU内蔵高速クロック(32MHz/36.864MHz/40MHz/50MHz※HOCOCR2で切替)
・LOCO CPU内蔵低速クロック(125kHz:初期状態)
の何れかを選択します。
標準ではLOCOに設定されています。

MOSCCR:メインクロック発振器コントロールレジスタ
SOSCCR:サブクロック発振器コントロールレジスタ
LOCOCR:低速オンチップオシレータコントロールレジスタ
HOCOCR:高速オンチップオシレータコントロールレジスタ
ILOCOCR:IWDT (ウォッチドッグ)専用オンチップオシレータコントロールレジスタ
それぞれのクロックのON-OFFを設定します

<内容>

49行目
クロック設定の保護解除
0xA50Bは 0xA500と0x000Bに分けれ考えます。
0xA50B0xa5000x000Bに分けて考え、上2桁0xA500は設定変更のためのパスワードの様なもの、下1桁0x000Bがどの部分の保護機能を解除するかを表します。
 0x0001:クロック発生回路関連レジスタへの書き込み許可
 0x0002:動作モード、消費電力低減機能、ソフトウェアリセット関連レジスタへの書き込み許可
 0x0008:LVD(電圧監視)関連レジスタへの書き込み許可
0x000Bだと全部保護解除になります。
今回の設定ではLVDを変更していないため0xA503としても動作します。

50行目※コメント行
今回は設定不要です。

51~57行目
分周器の設定をしています。
今回はメインとCMTで必要な周辺Bのみ設定しています。
メインを1分周器、周辺Bを2分周器に設定しています。

60行目
大元のクロックをメインクロック(20MHz)に設定しています。
分周器を合わせて考えると
メインの実動作周波数は20Mhz、周辺Bの実動作周波数は10MHzとなります。

64~68行目
必要なメインとサブのクロックを動作開始させています。
今回の設定ではサブ(32.768KHz)を利用していないのでサブを動作させる必要はありません。

以降は前回と同様なので割愛





タイマー関数部分
void timer(int msec){                   //10MCLK
    int cnt = 0;
    MSTP(CMT0)=0;                       //Wakeup CMT0, CMT1
    CMT0.CMCOR = 1249;                  //8msec ※実際は1ms
    CMT0.CMCR.WORD = 0x0000;
    CMT.CMSTR0.BIT.STR0 = 1;
    while(1){
        while(CMT0.CMCNT != 0x00);
        cnt++;
        if(cnt == msec){
            cnt = 0;
            CMT.CMSTR0.BIT.STR0 = 0;
            break;
        }
        while(CMT0.CMCNT == 0x00);
    }   
}

<用語>

MSTP
これはハードウェアマニュアルを探しても出てきません。
プロジェクト内を全検索するとiodefine.h内で見つかります。
MSTPはマクロで、iodefine.hの6542行目から6544行目に記載されています。

MSTP(CMT0)
を元に戻していくと
_MSTP(_CMT0)
__MSTP(_CMT0)
MSTP_CMT0
となります。

更にこれはiodefine.hの6486行目で
SYSTEM.MSTPCRA.BIT.MSTPA15
であることが分かります。

ここまで来ればハードウェアマニュアルに説明が出てきます。
MSTPCRA:モジュールストップコントロールレジスタA
様々な機能を停止しておくレジスタです。
起動直後はほとんどの機能が停止された状態なので、使う機能は自分で停止解除する必要があります。

MSTPA15:コンペアマッチタイマ(ユニット0)モジュールストップ設定ビット
要するにCMT0の停止を管理しています。

CMCOR:コンペアマッチタイマコンスタントレジスタ
CMTで使用する比較対象(カウンタの上限値)です。

CMCR:コンペアマッチタイマコントロールレジスタ
CMTで使用するクロックの分周期器と割り込み機能を設定します。

CMSTR0:コンペアマッチタイマスタートレジスタ0
CMT0とCMT1のカウント開始・停止を制御します。

STR0:スタートレジスタ0
CMT0のカウント開始・停止を制御します。

<内容>

31行目~33行目
CMTの初期化
MSTP(CMT0)=0;
CMT0機能を起動しています。
機能が起動しただけで、タイマーはまだスタートしていません。

CMT0.CMCOR = 1249;
タイマーの上限値を設定しています。
コメントでは8ミリ秒となっていますが、他の部分と合わせて実際には1ミリ秒になります。
解説は後程。

CMT0.CMCR.WORD = 0x0000;
この設定では分周器がPCLK/8(8分周器)となります。
PCLK(B)のクロックを8回カウントするとCMT0のカウンタが1増えます。
PCLKBが実動10MHzなのでPCLKBは1クロックで0.0001ms(0.0000001s)です。
8クロックで1カウントとなると1カウントは0.0008msとなります。
1msに到達するためには1/0.0008=1250カウント必要です。
真面目に確認していませんが処理の都合上1250から1引いた1249をタイマーの上限値に設定しています。

割込み機能は停止されています。
詳しくはハードウェアマニュアルの「CMCR」の項を参照してください。

34行目
カウント開始

35行目~44行目
条件を満たす(**ミリ秒経過)まで無限ループ

36行目
条件を満たす(1ミリ秒経過)まで無限空ループ
CMCNTが0以外の間空ループします。
CMCNTが0になると空ループを抜けて次の行に行きます。

37行目~42行目
ループするたびにcntを1増加させ、設定値msecと一致したらカウンタを停止させ無限ループを抜けます。
breakするとwhile(1)の無限ループから抜けます

43行目
カウントが増えるまで無限空ループ
37行目から42行目の処理が速すぎてCMCNTのカウントが増える前にCMCNTが0の状態で何回もwhile(1)の無限ループの中を回ってしまうのを防ぐためにCMCNTのカウントが増えて0以外になってから次のサイクルに進みます。

気になる事

設定変更のために保護機能を解除していますが、変更後は再度保護をかけなおすのた正しい手順です。

関数を実行するたびに31行目から33行目の初期設定が実行されますが、本来1回だけ実行すれば良い内容なのでこの部分は別の初期化関数で実行させた方が良いと思う。

CMCNTはtimer関数を実行する度に初期化した方が良いと思う。

最初にwhile(1)に突入した時点では処理が速すぎて36行目に来た時点でCMCNTが0ですぐ無限空ループを抜けてしまいそう。
while(CMT0.CMCNT != 0x00); の前に while(CMT0.CMCNT == 0x00); を書いておいた方が良い気がする。

PR

コメント

プロフィール

HN:
ぼんどF博士
性別:
男性

最新コメント