CCWO Embedded Space

CCWOの日々の開発を発信するブログ

RX631開発 Timer割り込み

マイコンにおいて必要不可欠なタイマー割り込みの設定をします。
10msと5msのタイマー割り込みを設定してLEDを点滅させてみましょう。
タイマー割り込みとは、文字通り現在行っているプログラムにタイマー的(周期的)に割り込んでプログラムを実行する機能です。
RX631マイコンや多くのマイコンは、シングルコアのCPUを持っています。そのため、一度に実行できるプログラムはただひとつのみです。(周辺機能はハードウェア的にレジスタに設定された動作を行っていますが、これはあくまで周辺機能なのでプログラムを実行しているCPUはプログラムをただひとつ実行していると表現しています。)
たとえばスイッチの入力を待つなどのプログラムを処理したい場合に、main文にてボタンの入力が来るのをずっと待っているプログラムを書くのは非常に非効率的です。(ボタンの入力を待つ以外の処理がなにもできなくなるため。)
そこで、割り込みを用いて、ボタンの入力がきたときだけ、実行する割り込みプログラムを別で用意しておくことができます。
ここではボタンの入力ではなく、周期的に呼ばれる割り込みプログラムを用意し、10msと5msおきに実行できるようにします。
まずはCMT.cとCMT.hを作成します。詳細は後述します。
CMT.h

#ifndef CMT_H_
#define CMT_H_

void CMT0_initialize(void);
void CMT1_initialize(void);

#endif /* CMT_H_ */

CMT.c

#include "iodefine.h"
#include "machine.h"
#include "vect.h"
#include "CMT.h"

#pragma interrupt (timer0(vect=VECT(CMT0, CMI0)))
#pragma interrupt (timer1(vect=VECT(CMT1, CMI1)))

void timer0(void){
	static unsigned char timer0cnt;

	if(timer0cnt == 10){
		PORTE.PODR.BIT.B1 ^= 1;
		timer0cnt = 0;
	}
	timer0cnt++;
}

void timer1(void){
	static unsigned char timer1cnt;

	if(timer1cnt == 10){
		PORTE.PODR.BIT.B0 ^= 1;
		timer1cnt = 0;
	}
	timer1cnt++;
}

void CMT0_initialize(void){
	// CMT0
	SYSTEM.PRCR.WORD = 0xA502;	// Release protect
	MSTP(CMT0) = 0;				// Wake up CMT0
	SYSTEM.PRCR.WORD = 0xA500;	// Protect
	CMT.CMSTR0.BIT.STR0 = 0;	// Disable CMT0 count
	CMT0.CMCR.BIT.CKS = 0;		// PCLK/8
	CMT0.CMCR.BIT.CMIE = 1;		// Enable CMIE
	CMT0.CMCNT = 0x0000;		// Clear Count
	CMT0.CMCOR = 29999;			// 5ms = (8/PCLK)*(CMCOR+1) >> CMCOR = ((5m*48M)/8)-1
	CMT.CMSTR0.BIT.STR0 = 1;	// Enable CMT0 count

	IEN(CMT0, CMI0) = 1;
	IPR(CMT0, CMI0) = 3;
}

void CMT1_initialize(void){
	// CMT1
	SYSTEM.PRCR.WORD = 0xA502;	// Release protect
	MSTP(CMT1) = 0;				// Wake up CMT1
	SYSTEM.PRCR.WORD = 0xA500;	// Protect
	CMT.CMSTR0.BIT.STR1 = 0;	// Disable CMT1 count
	CMT1.CMCR.BIT.CKS = 0;		// PCLK/128
	CMT1.CMCR.BIT.CMIE = 1;		// Enable CMIE
	CMT1.CMCNT = 0x0000;		// Clear Count
	CMT1.CMCOR = 59999;			// 10ms = (8/PCLK)*(CMCOR+1) >> CMCOR = ((10m*48M)/8)-1
	CMT.CMSTR0.BIT.STR1 = 1;	// Enable CMT1 count

	IEN(CMT1, CMI1) = 1;
	IPR(CMT1, CMI1) = 4;
}

つぎに、hardware_setup.cにてCMT.hをインクルードし、HardwareSetup関数でCMT.cのCMT0_initialize関数とCMT1_initialize関数を呼びます。
つぎに、vect.hの

// CMT0 CMI0
#pragma interrupt (Excep_CMT0_CMI0(vect=28))
void Excep_CMT0_CMI0(void);

// CMT1 CMI1
#pragma interrupt (Excep_CMT1_CMI1(vect=29))
void Excep_CMT1_CMI1(void);

をすべてコメントアウトします。
これでビルドし、書き込みましょう。LEDが点滅すれば成功です。

つぎに、タイマー割り込みの設定を説明します。
タイマー割り込みを行うためにはコンペアマッチタイマーというモジュール(周辺機能)を使用します。
今回、CMT0とCMT1を使用しますが、設定値の違いはタイマカウントのクリア値だけなので、CMT0について説明します。
以下の手順で設定します。
1.消費電力低減機能の解除
2.ピンの設定(今回は使用するモジュールとともに使うピンはないので不要。)
3.CMTの設定
4.ユーザー関数の定義(今回は割り込みのため、割り込み関数の定義となります。)

1.消費電力低減機能の解除

SYSTEM.PRCR.WORD = 0xA502;	// Release protect
MSTP(CMT0) = 0;				// Wake up CMT0
SYSTEM.PRCR.WORD = 0xA500;	// Protect

SCIの設定同様に、アクセスロックを解除し、消費電力低減機能を解除し、再度ロックします。

2.ピンの設定(略)

3.CMTの設定
f:id:CCWO:20160819160937p:plain
データシートp.1089の図28.2CMCNTカウンタの動作を参考に、設定します。

CMT.CMSTR0.BIT.STR0 = 0;	// Disable CMT0 count
CMT0.CMCR.BIT.CKS = 0;		// PCLK/8
CMT0.CMCR.BIT.CMIE = 1;		// Enable CMIE
CMT0.CMCNT = 0x0000;		// Clear Count
CMT0.CMCOR = 29999;			// 5ms = (8/PCLK)*(CMCOR+1) >> CMCOR = ((5m*48M)/8)-1
CMT.CMSTR0.BIT.STR0 = 1;	// Enable CMT0 count

まず、
・CMT0のカウントフラグをクリア(0にする)
・カウントアップ周波数の設定
・割り込みコントローラの設定
・CMT0のカウントをクリア(0にする)
・10msになるようにタイマーカウントをクリア値を設定
・タイマーカウントの開始
これでCMT0の設定は完了です。カウントが開始され、カウントが29999になったときにカウントがクリアされ、このとき割り込みプログラムが呼ばれ、またカウントアップを開始します。そして、この動作を継続します。

4.ユーザー関数の定義
割り込みプログラムを定義します。
ここでRX631にはICU(割り込みコントローラ)機能が存在し、割り込みはそこで一括して管理されています。
まずICUにて、各割り込みのEnable(利用可能)とPriority(優先順位)を設定します。MSTPと同様に、IERレジスタとIPRレジスタが各割り込みごとにiodefine.hでマクロされているのでこれを使用します。

IEN(CMT0 CMI0) = 1;    // Interrupt Enable
IPR(CMT0, CMI0) = 3;    // Interrupt Priority

IENにてCMT0の割り込みであるCMI0をEnableにし、割り込み優先レベル(小さい方が高レベル)を設定します。
各周辺機能の割り込みはデータシートp.387の表15.3割り込みベクタテーブルに一覧化されているので、参考にしましょう。
これで割り込みの設定が完了しました。
次に、ベクタテーブルの設定をします。

#include "iodefine.h"
#include "vect.h"

#pragma interrupt (timer0(vect=VECT(CMT0, CMI0)))
#pragma interrupt (timer1(vect=VECT(CMT1, CMI1)))

すべての割り込みはベクタテーブルで管理されており、RX631ではvect.hにて、割り込みで呼ばれるプログラムがすでに定義されています。
ここで、わかりやすいようにvect.hのCMI0,CMI1割り込み部分をコメントアウトし、CMT.cでタイマー割り込みのプログラムを完結できるように、vect.hをインクルードし、こちらで割り込み関数を定義します。
定義するためには

#pragma interrupt ((割り込み関数名)(vect=(ベクタ番号)))

で割り込み関数を定義します。
(関数名)に呼びたい関数名を定義し、ベクタ番号に呼ぶ呼び出す関数のベクタ番号を設定します。
ここでもIEN,IPRと同様に、ベクタ番号がiodefine.hでマクロされているのでこれを使用します。
これで割り込み関数の準備ができました。
また、多重割り込み(割り込み中の割り込み)を許可したい場合は(初期設定では不許可になっている。)

#pragma interrupt ((割り込み関数名)(vect=(ベクタ番号)),enable)

としたり、割り込み関数中で setpsw_i(); を呼ぶことで多重割り込みを許可できます。
これでタイマー割り込みが可能になったかと思います。以下割り込みも同様の手順のため省略します。
また、詳しい割り込みの動作については「RXショートセミナー」等の資料を参考にすると理解が深まると思います。

20160818の記事