60歳からの電子工作ノート

生涯学習として取り組んでいます。

ペルチェ制御用ボードの試作(ウェイト処理,タイマ割り込み,ΔΣA/Dコンバータ)

概要

ウェイト処理、タイマ割り込み、24ビットΔΣ(デルタシグマ)A/Dコンバータのテストプログラムを作成しました。

ウェイト処理

ウェイト時間が1mseのプログラムを作成します。1msec後、出力ポート(P31)をHighにします。また1msec後にLowにします。この動作をくりかえします。

f:id:vABC:20211220130849p:plain
Fig. ウエイト処理(1msec)

・test1n.c

#include	<machine.h>
#include	 "iodefine.h"
#include	 "misratypes.h"
#include	"debug_port.h"

void  delay_msec( uint32_t  n_msec );
static void debug_port_ini(void);

void main(void)
{
	debug_port_ini();	// P31を出力ポートに設定 (デバック用)
	
	while(1) {
	    
	    delay_msec( 1 );	// 1 msec
		
            DEBUG_0_PODR = 1;   // P31 = High
	    
	    delay_msec( 1 );	// 1 msec
  
	    DEBUG_0_PODR = 0;   // P31 = Low
	}
	
}


//   N [msec] 待つ関数 (ICLK = 32MHz)
//   命令語は、 RX V2 アーキテクチャ
//    コンパイル時 最適化レベル = 2   (-optimize = 2 )
//   n_msec=  1:  1msec
//      

#pragma instalign4 delay_msec

void  delay_msec( unsigned long  n_msec )
{
	unsigned long delay_cnt;

	while( n_msec-- ) {

	   delay_cnt = 10656;
		
	   while(delay_cnt --) 
	   { 			 
	   }

	}
}

//   デバックポートの設定 
//   (debug_port.h)

static void debug_port_ini(void)
{	
        DEBUG_0_PMR = 0;    //  P31 汎用入出力ポート
	DEBUG_0_PDR = 1;    //  出力ポートに指定
	
}


・debug_port.h

// Debug Port0用出力ポート(P31)の定義
#define DEBUG_0_PMR      (PORT3.PMR.BIT.B1)   //  汎用入出力ポート
#define DEBUG_0_PDR      (PORT3.PDR.BIT.B1)   //  出力ポートに指定
#define DEBUG_0_PODR     (PORT3.PODR.BIT.B1)  //  出力データ
#define DEBUG_0_PIDR     (PORT3.PIDR.BIT.B1)   //  ポート入力データ

コンパイル時 最適化レベル=2でコンパイルしています。(デフォルト)

f:id:vABC:20211220131325p:plain
Fig. コンパイル時の最適化レベル


・ #pragma instalign4
資料1*1の「(11) 分岐先の命令実行向け整合を行う関数の指定(p.382)」と、
資料2*2に記載されています。
指定した場合、コンパイラが適切と判断した際に自動的にNOP命令を追加して、CPUのキューを効率よく動かし実行速度を上げるようです。
仕様がよくわかりませんが、今回の条件ではNOP命令の追加は見当たりませんでした。main()からdelay_msec()までの逆アセンブル表示です。

f:id:vABC:20211222152417p:plain
Fig. 逆アセンブル表示(mainからdelay_msecまで)

アセンブラでの処理
資料3*3インラインアセンブラで作成した例が記載されていました。最初はこれを参考にして1msecのウェイト処理を作成しました。しかしウェイト時間が0.8msecになってしまいました。資料の対象デバイスを見ると CPUコアがRXv1でした。使用しているCPU(RX23E-A)はRXv2です。RXv2ではパイプライン処理が強化され命令の実行速度が速くなったためでした。

・時間の測定
実行時間の測定はオシロを使用せずに、CS+だけでも測定可能です。資料4*4と資料5*5の「2.14 実行時間の計測」に記載されています。
   

タイマ割り込み

出力ポート(P31)の出力を100msec毎に反転させるプログラムを作成します。コンペアマッチタイマ(CMT)により10msecをカウントします。10回カウントしたら、100msecフラグをONにして、メイン側に100msec経過した事を知らせます。

f:id:vABC:20211221084605p:plain
Fig. タイマ割り込みによる100msec毎のHigh/Low信号

・コンペアマッチタイマ(CMT) とレジスタ

f:id:vABC:20211222154216p:plain
Fig. コンペアマッチタイマと設定レジスタ

・test1n.c

#include	<machine.h>
#include	 "iodefine.h"
#include	 "misratypes.h"
#include	"debug_port.h"
#include	 "timer.h"

void clear_module_stop(void);
void  delay_msec( uint32_t  n_msec );
static void debug_port_ini(void);

void main(void)
{
	debug_port_ini();	// P31を出力ポートに設定 (デバック用)
	clear_module_stop();	//  モジュールストップの解除
	Timer10msec_Set();	//  10msec タイマ(CMT0)設定
	Timer10msec_Start();	//  10msec タイマカウント開始
	
	while(1) {
	   if  ( flg_100msec_interval == 1 ) { // 100msec経過した場合
		flg_100msec_interval = 0;	// 100msec経過フラグのクリア
		DEBUG_0_PODR = ~DEBUG_0_PIDR;	// 現在の出力を反転して出力
	     }
	}

}



// モジュールストップの解除
// CMTユニット0(CMT0, CMT1)

void clear_module_stop(void)
{
	SYSTEM.PRCR.WORD = 0xA50F;	// クロック発生、消費電力低減機能関連レジスタの書き込み許可	
	MSTP(CMT0) = 0;			// CMTユニット0(CMT0, CMT1) モジュールストップの解除
	SYSTEM.PRCR.WORD = 0xA500;	// クロック発生、消費電力低減機能関連レジスタ書き込み禁止
}

//   N [msec] 待つ関数 (ICLK = 32MHz)
//   命令語は、 RX V2 アーキテクチャ
//    コンパイル時 最適化レベル = 2   (-optimize = 2 )
//   n_msec=  1:  1msec
//      

#pragma instalign4 delay_msec

void  delay_msec( unsigned long  n_msec )
{
	unsigned long delay_cnt;
	while( n_msec-- ) {
	   delay_cnt = 10656;
	   while(delay_cnt --) 
	   { 			 
	   }
	}
}

//   デバックポートの設定 
//   (debug_port.h)
static void debug_port_ini(void)
{	
        DEBUG_0_PMR = 0;    //  P31 汎用入出力ポート
	DEBUG_0_PDR = 1;    //  出力ポートに指定
}

・timer.c

#include "typedefine.h"
#include  "iodefine.h"
#include "misratypes.h"
#include "timer.h"
#include "debug_port.h"

// Timer 
volatile uint8_t flg_100msec_interval;	// 100msec毎にON
volatile uint8_t timer_a_cnt;          //  100msecまでカウント (10msec毎にカウントアップ)

//  コンペアマッチタイマ CMT0
//   10msec毎の割り込み
//
#pragma interrupt (Excep_CMT0_CMI0(vect=28))

void Excep_CMT0_CMI0(void){
	
	timer_a_cnt++;	       // カウントのインクリメント
	
	if ( timer_a_cnt > 9 ) {	// 100msec経過
	       flg_100msec_interval = 1;  // 100msecフラグ ON
		timer_a_cnt = 0;	//  カウンターのクリア
	}
}
	
//
//    10msec タイマ(CMT0)
//    CMTユニット0のCMT0を使用。 
//
//  PCLKB(=32MHz)の128分周でカウント 32/128 = 1/4 MHz
//      1カウント = 4/1 = 4 [usec]  
//    1*10,000 usec =  N * 4 usec 
//      N = 2500


void Timer10msec_Set(void)
{	
	IPR(CMT0,CMI0) = 3;		// 割り込みレベル = 3  (15が最高レベル)
	IEN(CMT0,CMI0) = 1;		// CMT0 割込み許可
		
	CMT0.CMCR.BIT.CKS = 0x2;        // PCLKB/128       
	CMT0.CMCOR = 2499;		// CMCNTは0からカウント 	


}


//   CMT0 タイマ開始 
//  割り込み許可してカウント開始

void Timer10msec_Start(void)
{	
	CMT0.CMCR.BIT.CMIE = 1;		// コンペアマッチ割込み 許可
	CMT.CMSTR0.BIT.STR0 = 1;	// CMT0 カウント開始
	timer_a_cnt = 0;		//  タイマのカウント値クリア
}


・timer.h

extern volatile uint8_t flg_100msec_interval;
void Timer10msec_Set(void);
void Timer10msec_Start(void);

・intprg.c
割り込み処理をコメント化します。
・vect.h
#pragma の部分をコメント化します。

・ファイル一覧

f:id:vABC:20211222090519p:plain
Fig. ファイル一覧(タイマ割り込み)

・資料6 *6の「25. コンペアマッチタイマ(CMT) 」)を使用しています。
・コンペアマッチタイマ(CMT)の使用には、モジュールストップの解除が必要です(資料 6 「11.消費電力低減機能」)
・資料7 *7の「5.1 コンペアマッチタイマ」にインターバルタイマの例が記載されています。

24ビットΔΣ A/Dコンバータ

RX23E-Aにはゲインアンプ付きの24ビットΔ-Σ A/Dコンバータ(DSADA)が2ユニット(DSAD0とDSAD1)あります。1ユニット(DSAD0)の1チャンネル(ch0)に電圧を入力し、A/D変換値と変換時間を調べます。この例では100msec毎にA/D変換を開始し、1チャンネル分のA/D変換完了割り込みでA/D値を取得します。全チャンネル(この例では1チャンネル)のスキャン終了割り込みでA/D変換完了とします。

参考:
1)資料6の「33. アナログフロントエンド(AFE)」と「34. 24 ビットΔ-Σ A/D コンバータ(DSADA)」
2)資料8*8の「2.1 DSAD・AFEへの入力」

f:id:vABC:20211221113754p:plain
Fig. ΔΣ(デルタシグマ)A/Dコンバータ(今回の実験用)
f:id:vABC:20211221125129p:plain
Fig.ΔΣA/Dコンバータのテスト(フロー図と結果)

・設定内容

f:id:vABC:20211221130037p:plain
Fig. DSAD設定内容
f:id:vABC:20211222094502p:plain
Fig.レジスタの設定(DSAD)

・test1n.c

#include	<machine.h>
#include	 "iodefine.h"
#include	 "misratypes.h"
#include	"debug_port.h"
#include	 "timer.h"
#include	"dsad.h"

void clear_module_stop(void);
void  delay_msec( uint32_t  n_msec );
static void debug_port_ini(void);

void main(void)
{
	debug_port_ini();	// P31を出力ポートに設定 (デバック用)
	clear_module_stop();	//  モジュールストップの解除
	Timer10msec_Set();	//  10msec タイマ(CMT0)設定
	Timer10msec_Start();	//  10msec タイマカウント開始
	afe_ini();		// AFE(アナログフロントエンド)設定
	dsad0_ini();		// DASD0の設定 
	while(1) {
		
	   if  ( flg_100msec_interval == 1 ) { // 100msec経過した場合
		flg_100msec_interval = 0;	// 100msec経過フラグのクリア

		dsad0_start();			// DSAD0開始
		DEBUG_0_PODR = 1;		// P31 = ON
	    }

	   if ( dsad0_scan_over == 1 ) {	// dsad0 スキャン完了の場合
	   	dsad0_stop();			// DSAD0停止
		DEBUG_0_PODR = 0;		// P31 = OFF
	   }
	}
}



// モジュールストップの解除
// CMTユニット0(CMT0, CMT1)
// アナログフロントエンド(AFE)
// 24ビットΔ-Σ A/D コンバータ(DSAD0) ユニット0 

void clear_module_stop(void)
{
	SYSTEM.PRCR.WORD = 0xA50F;	// クロック発生、消費電力低減機能関連レジスタの書き込み許可	
	
	MSTP(CMT0) = 0;			// コンペアマッチタイマ(CMT) ユニット0(CMT0, CMT1) モジュールストップの解除
	MSTP(AFE) = 0;			// アナログフロントエンド(AFE) モジュールストップの解除
	MSTP(DSAD0) = 0;		// 24 ビットΔ-Σ A/D コンバータ(DSAD0) ユニット0 モジュールストップの解除
	
	SYSTEM.PRCR.WORD = 0xA500;	// クロック発生、消費電力低減機能関連レジスタ書き込み禁止
}




//   N [msec] 待つ関数 (ICLK = 32MHz)
//   命令語は、 RX V2 アーキテクチャ
//    コンパイル時 最適化レベル = 2   (-optimize = 2 )
//   n_msec=  1:  1msec
//      

#pragma instalign4 delay_msec

void  delay_msec( unsigned long  n_msec )
{
	unsigned long delay_cnt;

	while( n_msec-- ) {

	   delay_cnt = 10656;
		
	   while(delay_cnt --) 
	   { 			 
	   }

	}
}



//   デバックポートの設定 
//   (debug_port.h)

static void debug_port_ini(void)
{	
        DEBUG_0_PMR = 0;    //  P31 汎用入出力ポート
	DEBUG_0_PDR = 1;    //  出力ポートに指定
	
}

・dsad.c

#include "typedefine.h"
#include  "iodefine.h"
#include "misratypes.h"

#include "debug_port.h"
#include "dsad.h"
    
    
// AD data (DSAD0)
volatile uint8_t ad_ch;		// 変換チャネル: 格納されているデータがどのチャネルのA/D 変換結果かを示す。
				// ( 0=未変換またはデータ無効, 1=チャネル0のデータ, 2=チャネル1のデータ, 3=チャネル2のデータ, 4=チャネル3のデータ)
volatile uint8_t ad_err;	// 0:異常なし, 1:異常検出
volatile uint8_t ad_ovf;	// 0:正常状態(範囲内), 1:オーバフロー発生

volatile uint32_t ad_cnt;	// A/Dカウント値          
volatile int32_t ad_data;       //  A/Dデータ(2の補数形式)

volatile int32_t ad_ch0_data;	// DSAD0 Ch0のデータ
volatile uint32_t dsad0_scan_over;	// dsad0 ch0のスキャン完了
volatile float ch0_volt;
volatile float ch0_volt_mili;

//  DSAD0 AD変換完了 割り込み
// チャンネル毎のA/D変換終了で発生。
//  16.6 msec毎に発生
//
// 複数チャンネルを変換する場合、各チャンネルは初回変換となるため、デジタルフィルタの安定時間(4T)かかる。
// 4*T=4x4=16[msec] かかる。(T=OSR/0.5 = 2048/(0.5MHz) = 4 [msec])
// (参考 :アプリケーションノート 「RX23E-Aグループ AFE・DSADの使い方」1.2 チャネル機能を使用した複数信号のサンプリング )
//
#pragma interrupt (Excep_DSAD0_ADI0(vect=206))
void Excep_DSAD0_ADI0(void)
{
					 
	ad_ch  = DSAD0.DR.BIT.CCH;	// 変換チャネル( 0=未変換またはデータ無効, 1=チャネル0のデータ, 2=チャネル1のデータ)
	ad_err =  DSAD0.DR.BIT.ERR;	// 0:異常なし, 1:異常検出
	ad_ovf = DSAD0.DR.BIT.OVF;	// 0:正常状態(範囲内), 1:オーバフロー発生
	
	ad_cnt = DSAD0.DR.BIT.DATA;		// A/D変換後のデータ (32bit符号付きデータ)

	if (( ad_cnt & 0x800000 ) == 0x800000 ) {      // 24bit符号付きデータにする。
		ad_data =  ad_cnt - 16777216;		// 2^24 = 16777216
	}
	else{
		ad_data = ad_cnt;
	}
	
	if ( ad_ch == 1 ) {				// チャンネル0のデータ格納	
		ad_ch0_data = ad_data;
	}

	
}

// DSAD0 スキャン完了割り込み
//
#pragma interrupt (Excep_DSAD0_SCANEND0(vect=207))
void Excep_DSAD0_SCANEND0(void)
{
	dsad0_scan_over = 1;		// dsad0 スキャン完了
	
}



// DSAD0の 開始 
//
void  dsad0_start(void)
{
	DSAD0.ADST.BIT.START = 1;	// DSAD0 オートスキャン開始
	
	dsad0_scan_over = 0;
}


// DSAD0の 停止
//
void  dsad0_stop(void)
{
	 DSAD0.ADSTP.BIT.STOP = 1;	// DSAD0 オートスキャン停止
	 
	 while ( DSAD0.SR.BIT.ACT == 1 ) {    // オートスキャン実行中はループ。(オートスキャン停止待ち)
	 }
	 
}


// AFE(アナログフロントエンド)初期設定 
//
// 端子: 用途
//
//・DSAD0への入力信号設定
//  AIN4/REF1N: チャンネル0 -側
//  AIN5/REF1P: チャンネル0 +側
//
void afe_ini(void)
{
	   
     				// DSAD0への入力信号の設定
    				// チャネル0(回路図のラベルは Ch1)
    AFE.DS00ISR.BIT.NSEL = 4;   // AIN4:チャンネル0 -側 (Ch1_N) 			
    AFE.DS00ISR.BIT.PSEL = 5;	// AIN5:チャンネル0 +側 (Ch1_P) 
    AFE.DS00ISR.BIT.RSEL = 0x04;   // 基準電圧 +側 REFOUT(2.5V) , -側 AVSS0 ,リファレンスバッファ無効
    
    
    AFE.OPCR.BIT.TEMPSEN = 0;    // 温度センサ(TEMPS) の動作禁止
    AFE.OPCR.BIT.VREFEN = 1;	// 基準電圧源動作許可 (REFOUT 端子からVREF で生成された電圧(2.5 V) が出力) (安定まで、1msecかかる。)
    AFE.OPCR.BIT.VBIASEN = 0;   // バイアス電圧生成回路(VBIAS) の動作禁止
    AFE.OPCR.BIT.IEXCEN = 0;	// 励起電流源(IEXC)動作禁止
    
    AFE.OPCR.BIT.DSAD0EN = 1;	// DSAD0 動作許可 (このビットを“1” にしてからDSAD0 が起動するまで、400 μs 必要)
    
    AFE.OPCR.BIT.DSAD1EN = 0;	// DSAD1 動作 禁止
    
    AFE.OPCR.BIT.DSADLVM = 1;	// DSAD動作電圧選択  0: AVCC0=3.6~5.5 V, 1:AVCC0 = 2.7~5.5 V

    delay_msec(1);		// 1 msec待ち
 
}

//
// DASD0(デルタシグマ(ΔΣ)A/Dコンバータ)の初期化
//   チャンネル0   : A/D変換する
//   チャンネル1~5: A/D変換しない
//
void dsad0_ini(void){
    
    DSAD0.CCR.BIT.CLKDIV = 7;	// PCLKB/8  (DSADは、ノーマルモードでは4MHzで動作する。PCLKB=32MHzから、4MHzを生成するため8分周)
    DSAD0.CCR.BIT.LPMD = 0;	// ノーマルモード (モジュレータクロック周波数(fMOD) = 500[kHz] = 0.5[MHz] )
    
    DSAD0.MR.BIT.SCMD = 1;	// 0:連続スキャンモード, 1:シングルスキャンモード
    DSAD0.MR.BIT.SYNCST = 0;	// ユニット間(DSAD0,DSAD1)同期スタートの無効
    DSAD0.MR.BIT.TRGMD = 0;	// ソフトウェアトリガ(ADSTレジスタへの書き込みで変換開始)
    DSAD0.MR.BIT.CH0EN = 0;	// チャンネル0 A/D変換する
    DSAD0.MR.BIT.CH1EN = 1;	// チャンネル1 A/D変換しない
    DSAD0.MR.BIT.CH2EN = 1;	// チャンネル2 A/D変換しない
    DSAD0.MR.BIT.CH3EN = 1;	// チャンネル3 A/D変換しない
    DSAD0.MR.BIT.CH4EN = 1;	// チャンネル4 A/D変換しない
    DSAD0.MR.BIT.CH5EN = 1;	// チャンネル5 A/D変換しない
    
    				// チャンネル0の動作モード設定
    DSAD0.MR0.BIT.CVMD = 0;	// 通常動作
    DSAD0.MR0.BIT.SDF = 0;	// 2の補数形式(バイナリ形式) -8388608 (80 0000h) ~ +8388607(7F FFFFh)
				// DSADへの入力電圧 = (Vref * 2/Gain) * DR_DATA/(2^24) , 2^24 = 16,777,216
    
    DSAD0.MR0.BIT.OSR = 5;	// オーバーサンプリング比 = 2048
    DSAD0.MR0.BIT.DISAP = 0;	// +側入力信号断線検出アシスト なし
    DSAD0.MR0.BIT.DISAN = 0;    // -側入力信号断線検出アシスト なし
    DSAD0.MR0.BIT.AVMD = 0;	// 平均化処理なし
    DSAD0.MR0.BIT.AVDN = 0;	// 平均化データ数選択
    DSAD0.MR0.BIT.DISC = 0;     // 断線検出アシスト電流 = 0.5 [uA]
    
    
    
    // デジタルフィルタ処理時間(T)
    //    T = オーバーサンプリング比(OSR) / モジュレータクロック周波数(fMOD)
    //     OSR = 2048
    //     fMOD = 0.5 [MHz] ( ノーマルモード )
    //    T = 2048 / 0.5 = 4 [msec]
    //
    //  A/D変換時間(セトリング時間)  (マニュアル 34.3.7.2 セトリング時間)
    //    4 * T + 256[usec] = 16.3 msec
    //
    
				// チャンネル0  A/D変換回数,ゲイン設定    
    				// A/D 変換回数 N = x * 32 + y 、(CR0.CNMD = 1:即値モードの場合)
    DSAD0.CR0.BIT.CNY = 1;	// 
    DSAD0.CR0.BIT.CNX = 0;	//                                                        
    DSAD0.CR0.BIT.CNMD = 1;	// A/D変換回数演算モード :即値モード(A/D変換回数は1~255回)
    DSAD0.CR0.BIT.GAIN =0x10;	// PGA(プログラマブルゲイン計装アンプ)有効、ゲイン=1 アナログ入力バッファ(BUF) の有効
   
    IPR(DSAD0,ADI0) = 4;	// 割り込みレベル = 4  (15が最高レベル)
    IEN(DSAD0,ADI0) = 1;	// ADI0(A/D変換完了) 割込み許可
    
    IPR(DSAD0,SCANEND0) = 5;	// 割り込みレベル = 5  (15が最高レベル)
    IEN(DSAD0,SCANEND0) = 1;	// スキャン完了 割込み許可
   
}


・dsad.h

void Excep_DSAD0_ADI0(void);
void Excep_DSAD0_SCANEND0(void);
void  dsad0_start(void);
void  dsad0_stop(void);
void afe_ini(void);
void dsad0_ini(void);

extern volatile uint8_t ad_err;
extern volatile uint8_t ad_ovf;
extern volatile int32_t ad_ch0_data;
extern volatile uint32_t dsad0_scan_over;


・intprg.c
割り込み処理をコメント化します。
・vect.h
#pragma の部分をコメント化します。

・ファイル一覧

f:id:vABC:20211222093412p:plain
Fig. ファイル


・アナログフロントエンド(AFE)及び24 ビットΔ-Σ A/D コンバータ(DSADA)の使用には、モジュールストップの解除が必要です(資料 6 「11.消費電力低減機能」)

*1:資料1「CC-RX コンパイラ ユーザーズマニュアル (R20UT3248JJ0110 Rev.1.10)」

*2:資料2 「RXファミリ用C/C++コンパイラパッケージ CC-RX V3 プログラミング・テクニック編(R20AN0643JJ0100) 」

*3:資料3「RX ファミリ ソフトウェアによるウェイト処理のコーディング例 (R01AN1852JJ0100)」(注:RXv1用) 

*4:資料4「RX用CubeSuite+の実行時間計測機能について」

*5:資料5「CS+ V8.06.00 統合開発環境 ユーザーズマニュアル RX デバッグ・ツール編 (R20UT4977JJ0100 Rev.1.00)」

*6:資料6「RX23E-Aグループ ユーザーズマニュアル ハードウェア編 ( R01UH0801JJ0110 Rev.1.10) 」

*7:資料7「ルネサス半導体セミナー RX200 マイコンコース テキスト」

*8:資料8「RX23E-AグループAFE・DSADの使い方 (R01AN4799JJ0100 Rev.1.00) 」