忍者ブログ

Fグループ電子工作講座

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

RX220のCMTを割り込みで使用

前回は秋月サンプルのタイマープログラムの解説を行いました。
サンプルプログラムではCMTを利用して空ループによる待機を行っています。
これでは、空ループ中は他の処理ができません。
そこでCMTを割り込みとして使用します。
また、後で使いまわせるようにソースファイルを分割して作成します。
※関連:割り込み処理とはヘッダファイルを使いこなす

今回行うのは
・クロック設定機能を別ファイルに分割
・CMT機能一式を別ファイルに分割
・CMT機能の初期化と実行処理をきちんと分ける
・intprg.cへ割り込み関数を登録する
の4つとなります。

今回の内容は既に公開しているhwsetup_rx220.chwsetup_rx220.hおよびintCMT_rx220.cintCMT_rx220.hの解説です。


クロック設定機能を別ファイルに分割

クロック設定は他の機能とも連携する必要があるためCMT機能と一緒に設定するのではなく、独立して設定を行います。また、その他機能との兼ね合いから32MHz,1分周器としてCPUを動かします。
ファイルを4つ作ります。

ファイル作成

1) メモ帳などでテキスト(*.txt)を作製
2) ファイル → 名前を付けて保存
   ファイル名:hwsetup_rx220.c
   ファイルの種類:すべてのファイル
3) 保存

hwsetup_rx220.hの中身

#include "iodefine.h"
#ifndef _HW_SETUP_H_
    #define _HW_SETUP_H_ 1
#endif

void HardwareSetup(void);    //クロック設定、LEDとスイッチの初期化
void set_LED_R(int on);        //ベースボードLED赤ON-OFF
void set_LED_G(int on);        //ベースボードLED緑ON-OFF
int get_sw1(void);            //ベースボードスイッチ状態取得

ヘッダーファイルにはソースファイルで記述する関数のプロトタイプ宣言をしておきます。

hwsetup_rx220.cの中身

#include"hwsetup_rx220.h"

#define KEY_PRCR 0xA500    //プロテクト解除で使用するキー 0xA5 固定
#define PRCR_CLK 0x0001    //クロック関連
#define PRCR_MOD 0x0002    //動作モード、消費電力低減機能、ソフトウェアリセット関連
#define PRCR_LVD 0x0008    //LVD関連
#define PRCR_ALL (PRCR_CLK|PRCR_MOD|PRCR_LVD)    //全て

#define PROTECTOFF        (KEY_PRCR|PRCR_CLK|PRCR_MOD)
#define PROTECTON        (KEY_PRCR)

/*クロックを設定する*/
void HardwareSetup(void){
   
    SYSTEM.PRCR.WORD = (KEY_PRCR|PRCR_ALL);    //クロックソース選択の保護の解除
    //SYSTEM.OPCCR.BIT.OPCM = 0x02;            //動作モードの設定。低速/中速
    //SYSTEM.SCKCR.LONG = 0x00001412;        //マニュアル参照。
    //SYSTEM.SCKCR.BIT.PCKD    = 0x00;        //周辺モジュールクロックD 分周期
    //SYSTEM.SCKCR.BIT.PCKB    = 0x01;        //周辺モジュールクロックB 分周期(2分周)
    SYSTEM.SCKCR.BIT.PCKB    = 0x00;        //周辺モジュールクロックB 分周期(1分周)
    //SYSTEM.SCKCR.BIT.BCK    = 0x00;        //外部バスクロック 分周期
    SYSTEM.SCKCR.BIT.ICK    = 0x00;        //システムクロック 分周期。
    SYSTEM.SCKCR.BIT.FCK    = 0x00;        //FlashIFクロック 分周期。これを設定しないとデータフラッシュが使えない
   
    //クロックソースの選択
    SYSTEM.SCKCR3.WORD = 0x0100;        //大元のクロックに高速オンチップオシレータ32MHzを使用する。
    //SYSTEM.SCKCR3.WORD = 0x0200;        //大元のクロックにメインクロック20MHzを使用する。
    //SYSTEM.SCKCR3.WORD = 0x0300;        //大元のクロックにサブクロック32.768KHzを使用する。
   
    //クロックの元栓の設定
    SYSTEM.MOSCCR.BYTE = 0;            //メインクロック発振器動作開始
    SYSTEM.SOSCCR.BYTE = 0;            //サブクロック発振器動作開始
    //SYSTEM.LOCOCR.BYTE = 0;        //低速オンチップオシレータ動作開始
    SYSTEM.HOCOCR.BYTE = 0;            //高速オンチップオシレータ動作開始
    //SYSTEM.ILOCOCR.BYTE =1;        //IWDT専用オシレータ停止

    SYSTEM.PRCR.WORD = PROTECTON;    //クロックソース選択の保護

    //ポートHの設定
    PORTH.PODR.BYTE=0x00;              //出力をすべて0
    PORTH.PDR.BYTE=0xFB;            //PH2だけ入力                   

}

前回のサンプルプログラムとの違いは
・周辺モジュールクロックB 分周器を2→1へ変更した
・大元のクロックをメインクロック発信器から高速オンチップオシレータへ変更した
・高速オンチップオシレータの動作開始を行った
・設定後にPRCRの保護を設定した
といった感じです。
LVDの設定変更を行っていないので、15行目の(KEY_PRCR|PRCR_ALL)PROTECTOFFと書いた方が分かりやすいと思います。

LEDとスイッチの設定

/*============= LED赤の出力関数(0:消灯、1:点灯) =============*/
void set_LED_R(int on){
    if(on==0)    //関数への入力が0の時
        PORTH.PODR.BIT.B0=0;    //消灯
    else        //それ以外(入力が1の時)
        PORTH.PODR.BIT.B0=1;    //点灯
}

/*============= LED緑の出力関数(0:消灯、1:点灯) =============*/
void set_LED_G(int on){
    if(on==0)    //関数への入力が0の時
        PORTH.PODR.BIT.B1=0;    //消灯
    else        //それ以外(入力が1の時)
        PORTH.PODR.BIT.B1=1;    //点灯
}

/*============= SW1の状態取得関数(SW1上:0、SW1下:1)=============*/
int get_sw1(void){
    return PORTH.PIDR.BIT.B2;
}

前々回のLEDとスイッチ部分の処理を関数化しただけなので特に説明は要らないと思います。
if文は条件を満たした時に次に記述されているセミコロン;が出てくるまでの部分のみ処理されます。
ほとんどの場合複数行の処理を行うのでカッコ{}でくくって複数行を実行させます。
カッコを使わないのは行儀の悪い書き方らしいので、気になる方はカッコを追加してください。

CMT関連の設定

ここからが本番です。

intCMT_rx220.hの中身

#include "iodefine.h"
#ifndef _INT_CMT_RX220_H_
    #define _INT_CMT_RX220_H_ 1
#endif

void init_CMT0(int T);
void CMTU0_INT_CMT0(void);    //intprg.cへ登録が必要
unsigned long getsystime(void);

こちらもプロトタイプ宣言を行っているだけです。
機能的にはsh7125のプログラムと同様です。

intCMT_rx220.cの中身

#include"intCMT_rx220.h"

unsigned short LN;
unsigned long _systime;

#define KEY_PRCR 0xA500    //プロテクト解除で使用するキー 0xA5 固定
#define PRCR_CLK 0x0001    //クロック関連
#define PRCR_MOD 0x0002    //動作モード、消費電力低減機能、ソフトウェアリセット関連
#define PRCR_LVD 0x0008    //電圧監視関連
#define PRCR_ALL (PRCR_CLK|PRCR_MOD|PRCR_LVD)    //全て

#define PROTECTOFF        (KEY_PRCR|PRCR_MOD)
#define PROTECTON        (KEY_PRCR)

void init_CMT0(int T){
    _systime=0;

    SYSTEM.PRCR.WORD = PROTECTOFF;    //プロテクト解除
    MSTP(CMT0)=0;                    //CMT0モジュール停止解除
    SYSTEM.PRCR.WORD = PROTECTON;    //プロテクト
   
    IPR(CMT0, CMI0) = 0x7u;        //割込み優先順位
    IEN(CMT0, CMI0) = 0x1u;      //割込み許可
    IR(CMT0, CMI0) = 0x0;        //割込みフラグクリア(クリアしなくても動く)

//    LN=T*(1250-1);    // 0.8us×1250 → 1ms@20MHz,2分周期1/8φ
    LN=T*(1000-1);    // 1.0us×1000 → 1ms@32MHz,1分周期1/32φ
    LN=(LN>0xFFFF)?0xFFFF:LN;    //リミット
    CMT0.CMCOR = LN;            //上限値 0-0xFFFF(65535)

//    CMT0.CMCR.BIT.CKS=0;        //クロック1/8φ (20MHz,2分周期,1/8φ→0.8us)
    CMT0.CMCR.BIT.CKS=1;        //クロック1/32φ (32MHz,1分周期,1/32φ→1.0us)
    CMT0.CMCR.BIT.CMIE=1;        //コンペアマッチ割り込み許可
    CMT0.CMCNT=0;                //カウンタ初期化
    CMT.CMSTR0.BIT.STR0 = 1;    //CMT0カウント開始
}

//ベクタ番号28 intprg.c Excep_CMTU0_CMT0内で実行させる
void CMTU0_INT_CMT0(void){
    _systime++;
    //IR(CMT0, CMI0) = 0x0;

}

unsigned long getsystime(void){
    return _systime;
}

前回に対して
・初期化(init_CMT0)と実行部分(CMTU0_INT_CMT0)が分かれた
・設定変更後に保護を有効にした
・割り込み関連の初期化が増えた
・クロックカウントの分周器を1/8φから1/32φへ変更した
※クロックも変更したため、1カウント0.8usから1.0usへ変わった
といったところでしょうか。

空ループで待機させるのではなく、CPUが起動してからの内部時刻(ミリ秒単位)を随時計算し、他のプログラムでこの時刻を利用するようにします。

今回初出は    IPRIENIRCMIEの4つです。
IPRIENIRMSTP同様にマクロです。
ハードウェアマニュアルを探しても出てきません。
機能的には
IPR:割込み優先順位の設定
IEN:割込み許可
IR:割込みフラグの設定
です。

CMIE:コンペアマッチ割り込みの許可
です。
IENとの違いは
IEN:CMT0に関わる全ての割り込み機能のロック解除
CMIE:CMT0のコンペアマッチ割り込みを行うための許可を行う
となっており、CMT0全体のロック解除使う機能の個別解除の順に処理しています。
ただし、CMT0に関してはコンペアマッチ割り込み以外に割込み機能は無いようです。

1ms毎にCMTU0_INT_CMT0が実行されてシステム時刻が加算されていきます。
現在のシステム時刻はgetsystime関数で取得します。


ファイルの取り込み

作成したファイルはプロジェクトに取り込む必要があります。
取り込みかたはこちらを参照してください。


intprg.cへ割り込み関数を登録

割込み処理用に関数を作成しましたが、作成しただけでは関数は実行してもらえません。
割込みが発生した時に実行されるように登録する必要があります。

CMT0のコンペアマッチ割り込みが発生するとintprg.cの64行目Excep_CMTU0_CMT0が実行されます。
Excep_CMTU0_CMT0の中を見ると何もありません。そこで、Excep_CMTU0_CMT0へ作成した割込み関数を入れておきます。

// CMTU0_CMT0
void Excep_CMTU0_CMT0(void){ CMTU0_INT_CMT0(); }

これで
CMT0のコンペアマッチ割り込みが発生するとCMTU0_INT_CMT0が実行されます。


メイン処理の作成

作成した機能を実際に使ってみます。
メインのソースファイル(rx220_test.c等)を以下の様に変更します。

#include "hwsetup_rx220.h"
#include "intCMT_rx220.h"

void main(void)
{
    unsigned long timer;
    HardwareSetup();    //クロック初期化
    init_CMT0(1);        //CMT0初期化
    timer=getsystime();    //現在の時刻を取得

    set_LED_R(0);        //LED赤消灯
    set_LED_G(0);        //LED緑消灯

    while(1){
        if(timer<getsystime()){        //設定時刻を超えた(50ms毎に実行される)
            timer=getsystime()+50;    //タイマーを50ms後に設定

            if(getsystime()/1000%2){    //ミリ秒→秒に変換→2で割った余り(0か1)→1なら赤点灯、0なら赤消灯
                set_LED_R(1);        //LED赤点灯
            }else{
                set_LED_R(0);        //LED赤消灯
            }

            set_LED_G(get_sw1());    //スイッチで緑点灯

        }//end timer
    }//end while
}//end main

ロボット制御などでループの時間を一定にしたい場合に使用できます。
※今回のプログラムでは50ms毎に実行されるのはあまり関係ありません。
内部時刻を取得して1秒おきにLED赤の点灯と消灯を繰り返します。
また、空ループによる待機を行っていないので、スイッチにより任意のタイミングでLED緑を切り替える事が出来ます。
PR

コメント

プロフィール

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

最新コメント