Renesas RX621 Timer割り込み
Timer割り込みとは、メインのプログラム実行から周期的(タイマー的)に割り込みプログラムを実行する機能です。
このため、プログラムを周期的に行いたい時や何かイベントが発生した時に実行したい時に割り込みは使用されます。
今回は周期的にプログラムを実行するTimer割り込みについて説明していきます。
(イベント: AD変換完了、通信においてデータ受信or送信、インプットキャプチャ(状態変化)等があげられます。)
それでは、CMT(コンペアマッチタイマー)機能を用いて、Timer割り込みの設定をしていきたいと思います。
まず、e2 stduio左側プロジェクトエクスプローラのプロジェクトフォルダ内のsrcフォルダを右クリックし,
新規→ソース・ファイルからCMT.c
新規→ヘッダ・ファイルからCMT.h
を作成します。
ソースコードの追加はプロジェクトファイルのsrcフォルダに直接作成することでも可能です。
つぎに、hardware_setup.cにてCMT.hをインクルードし、HardwareSetup関数でCMT.cのCMT0_initialize関数とCMT1_initialize関数を呼びます。
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 CMT0_initialize(void); void CMT1_initialize(void); void timer0(void){ static unsigned char timer0cnt; if(timer0cnt == 10){ PORTA.DR.BIT.B2 ^= 1; timer0cnt = 0; } timer0cnt++; } void timer1(void){ static unsigned char timer1cnt; if(timer1cnt == 10){ PORTA.DR.BIT.B3 ^= 1; timer1cnt = 0; } timer1cnt++; } void CMT0_initialize(void) { // CMT0 5ms MSTP(CMT0) = 0; // Wake up CMT0 CMT0.CMCR.BIT.CMIE = 1; // Enable interrupt CMT0.CMCR.BIT.CKS = 0; // PCLK/8 CMT0.CMCOR = 29999; // Time interval 5ms IPR(CMT0, CMI0) = 3; // Interrupt level 3 IEN(CMT0, CMI0) = 1; // Enable CMT0 interrupt CMT.CMSTR0.BIT.STR0 = 1; // CMT0 counter start } void CMT1_initialize(void) { // CMT1 10ms MSTP(CMT1) = 0; // Wake up CMT1 CMT1.CMCR.BIT.CMIE = 1; // Enable interrupt CMT1.CMCR.BIT.CKS = 0; // PCLK/8 CMT1.CMCOR = 59999; // Time interval 10ms IPR(CMT1, CMI1) = 4; // Interrupt level 4 IEN(CMT1, CMI1) = 1; // Enable CMT1 interrupt CMT.CMSTR0.BIT.STR1 = 1; // Counter start }
つぎに、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が点滅すれば成功です。
それではCMTの設定を見ていきます。今回はCMT0,CMT1の設定の違いはタイマークリア値のみなので、CMT0について説明していきます。
- モジュールスタート(消費電力低減機能の解除)
- ピンの設定
- CMTの設定
- ユーザ関数の定義
1.モジュールスタート(消費電力低減機能の解除)
MSTP(CMT1) = 0; // Wake up CMT1
消費電力低減機能を解除します。RX631のようにアクセスロックはないので、そのまま解除します。
MSTPCTRレジスタがiodefine.hでMSTP(****)というようにデファインされているのでこれを使用します。
2.ピンの設定
今回CMTと連動するピンはないので設定はありません。
3.CMT0の設定
データシートp.1133の図22.3とその説明を参考にします。
CMT0.CMCR.BIT.CMIE = 1; // Enable interrupt CMT0.CMCR.BIT.CKS = 0; // PCLK/8 CMT0.CMCOR = 29999; // Time interval 5ms IPR(CMT0, CMI0) = 3; // Interrupt level 3 IEN(CMT0, CMI0) = 1; // Enable CMT0 interrupt CMT.CMSTR0.BIT.STR0 = 1; // CMT0 counter start
CMT0のTimer割り込みの設定は
CMCRレジスタの
CMIE: 1→割り込みを許可
CKS: 0→タイマカウント周期をPCLK/8
CMT0レジスタの
CMCOR: 29999→タイマクリアカウントを29999に設定し、5ms周期でタイマカウントがクリアされるようにする
CMTSTR0レジスタの
STR0: 1→CMT0のカウントをスタートする。
となり、割り込みの設定は
IPR(CMT0, CMI0) = 3; // Interrupt level 3 IEN(CMT0, CMI0) = 1; // Enable CMT0 interrupt
とします。RX621はICUにより、すべての割り込みが管理されています。
ICUのIENレジスタに各割り込みに対しての、許可ビットが存在し、IPRレジスタに各割り込みに対しての、割り込み優先度を設定するビットが存在します。
そしてこのIENレジスタとIPRレジスタは各割り込み毎にIPR(割り込み名)、IEN(割り込み名)というようにiodefine.hでマクロされていますのでこれを使用します。
これで割り込みを含め、Timer割り込みの設定が完了しました。
4.ユーザ関数の定義
#include "vect.h" #pragma interrupt (timer0(vect=VECT(CMT0, CMI0))) #pragma interrupt (timer1(vect=VECT(CMT1, CMI1)))
各割り込みで呼ばれる割り込み関数はvect.hですべて定義されています。
そのため、vect.hのCMT0とCMT1のコンペアマッチで発生する割り込み(コンペアマッチ割り込み)の該当部分をコメントアウトし、CMT.hでTimer割り込みすべてを完結できるようにします。
ここで
#pragma interrupt ((割り込み関数名)(vect=(割り込みベクタ番号)))
というように定義することで、ベクタ番号とベクタ番号に対応し割り込み発生時に呼ばれる関数を定義することができます。
ベクタ番号とは各割り込みに対して呼ばれる関数にすで番号が振られており、それをベクタ番号といい、VECT(割り込み名)でiodefine.hでマクロされているのでこれを使用します。
今回はCMT0のコンペアマッチ割り込みの関数としてtimer0を、CMT1のコンペアマッチ割り込みの関数としてtimer1を作成しました。
ここでtimer0関数は5ms毎に呼ばれ10回目でLEDを点滅させるので50msおきにLEDを点滅させます。
同様にtimer1関数は10ms毎に呼ばれ10回目でLEDを点滅させるので100msおきにLEDを点滅させます。
Timer割り込みの設定はこれで完了です。多重割り込み等は
embedded-blog.ccwo.net
を参照して下さい。
20160822の記事
Renesas RX621 UART
UARTを用いて、RX621のデバッグをします。
SIM I/Oを用いて今回はpritnfをSCI1にリダイレクト出来るようにします。
(printfを自身で作成するのもアリかと思います。)
回路はRX621→SCI1→FT231XS→COM(パソコン)となります。
embedded-blog.ccwo.net
手順はRX631と同様になるため、さくさく説明していきます!
まず、lowsrc.c、lowsrc.hとSCI.c、SCI.hという名前で次のファイルをプロジェクトに追加します。
(lowsrc.c、lowsrc.hは上記RX631のUARTの記事を参照)
e2 stduio左側プロジェクトエクスプローラのプロジェクトフォルダ内のsrcフォルダを右クリックし,
新規→ソース・ファイルからlowsrc.c
新規→ヘッダ・ファイルからlowsrc.h
新規→ソース・ファイルからSCI.c
新規→ヘッダ・ファイルからSCI.h
を作成します。
ソースコードの追加はプロジェクトファイルのsrcフォルダに直接作成することでも可能です。
つぎに、hardware_setup.cにてSCI.hをインクルードし、HardwareSetup関数でSCI.cのSCI1_initialize関数を呼びます。
SCI.h
#ifndef SCI_H_ #define SCI_H_ void SCI1_initialize(void); #endif /* SCI_H_ */
SCI.c
#include "iodefine.h" #include "machine.h" #include "SCI.h" void SCI1_initialize(void){ MSTP(SCI1) = 0; // Wake up SCI1 PORT3.ICR.BIT.B0 = 1; // Enable P30 receive buffer SCI1.SCR.BIT.TIE = 0; // Disable TXI SCI1.SCR.BIT.RIE = 0; // Disable RXI, ERI SCI1.SCR.BIT.TE = 0; // Disable TX SCI1.SCR.BIT.RE = 0; // Disable RX SCI1.SCR.BIT.MPIE = 0; // Normal Mode SCI1.SCR.BIT.TEIE = 0; // Disable TEIE SCI1.SCR.BIT.CKE = 0; // SCK is port SCI1.SMR.BIT.CM = 0; // Asynchronous communication method SCI1.SMR.BIT.CHR = 0; // 8bit SCI1.SMR.BIT.PM = 0; // None parity SCI1.SMR.BIT.STOP = 0; // None stop bit SCI1.SMR.BIT.MP = 0; // Disable multiple processor SCI1.SMR.BIT.CKS = 0; // PCLK SCI1.BRR = 12; // 115200bps nop(); SCI1.SCR.BIT.TE = 1; // Enable TX SCI1.SCR.BIT.RE = 1; // Enable RX } void charput(char c) { while(SCI1.SSR.BIT.TEND == 0); SCI1.TDR = c; SCI1.SSR.BIT.TEND = 0; } char charget(void){ char data; data = SCI1.RDR; SCI1.SSR.BIT.RDRF = 0; return data; }
次に、reset_program.c内の
//#ifdef __cplusplus // Use SIM I/O //extern "C" { //#endif //extern void _INIT_IOLIB(void); //extern void _CLOSEALL(void); //#ifdef __cplusplus //} //#endif
とreset_program.cのPowerON_Reset_PC関数内の
//_INIT_IOLIB(); //_CLOSEALL();
のコメントアウトを外します。
これで準備は完了しました。
main文にて、printf関数を用いてデバッグを行います。
#include "iodefine.h" #include "machine.h" #include <stdio.h> void main(void) { static unsigned int i; while(1){ printf("Hello RX World!\n"); for(i = 0; i < 65000; i++) nop(); } }
これでTeraTerm等でCOM接続をし、Hello RX World!と表示されれば成功です。
ただ、printfを高速で連続に呼び出すと、送信バッファが溜まってしまいますので、空関数nopを使ってdelayをとっています。
nop関数を使うためにはmachine.hをインクルードする必要があります。
それでは、SCI1の設定手順を見ていきます。
- モジュールスタート(消費電力低減機能の解除)
- ピンの設定
- SCI1の設定
- ユーザ関数の定義
1.モジュールスタート
MSTP(SCI1) = 0; // Wake up SCI1
消費電力低減機能を解除します。RX631のようにアクセスロックはないので、そのまま解除します。
MSTPCTRレジスタがiodefine.hでMSTP(****)というようにデファインされているのでこれを使用します。
2.ピンの設定
PORT3.ICR.BIT.B0 = 1; // Enable P30 receive buffer
P30の受信バッファを有効にします。RX631とくらべて、ピンひとつずつに周辺機能を割り当てる機能は一部にしか用意されておらず、モジュール開始と同時に切り替わる仕様であると思います。
3.SCI1の設定
データシート図29.7を参考に、レジスタを設定していきます。
SCI1.SCR.BIT.TIE = 0; // Disable TXI SCI1.SCR.BIT.RIE = 0; // Disable RXI, ERI SCI1.SCR.BIT.TE = 0; // Disable TX SCI1.SCR.BIT.RE = 0; // Disable RX SCI1.SCR.BIT.MPIE = 0; // Normal Mode SCI1.SCR.BIT.TEIE = 0; // Disable TEIE SCI1.SCR.BIT.CKE = 0; // SCK is port SCI1.SMR.BIT.CM = 0; // Asynchronous communication method SCI1.SMR.BIT.CHR = 0; // 8bit SCI1.SMR.BIT.PM = 0; // None parity SCI1.SMR.BIT.STOP = 0; // None stop bit SCI1.SMR.BIT.MP = 0; // Disable multiple processor SCI1.SMR.BIT.CKS = 0; // PCLK SCI1.BRR = 12; // 115200bps nop(); SCI1.SCR.BIT.TE = 1; // Enable TX SCI1.SCR.BIT.RE = 1; // Enable RX
ほぼ初期化手順通りで大丈夫です。
4.ユーザ関数の定義
今回はprintfにリダイレクトするため、SCI1でcharget,charputを定義しています。
他のSCIを用いてprintfをする場合はSCIの番号を変えれば可能です。
これでSCIの設定は完了です。
Renesas RX621 I/O
次にI/Oピンの設定をします。
出力ピンにPA2,PA3,PA4,PA5
入力ピンにPD0,PD1,PD2,PD3
を設定します。
hardware_setup.cにて以下のようにPin_initialzie関数を作成し、HardwareSetup関数にて呼び出します。
#include "iodefine.h" void Clock_initialize(void); void Pin_initialize(void); void HardwareSetup(void) { Clock_initialize(); Pin_initialize(); } void Clock_initialize(void) { SYSTEM.SCKCR.BIT.ICK = 0x0; // ICLK: XTAL*8 SYSTEM.SCKCR.BIT.PCK = 0x1; // PCLK: XTAL*4 SYSTEM.SCKCR.BIT.BCK = 0x2; // BCLK: XTAL*2 } void Pin_initialize(void) { PORTD.DDR.BIT.B0 = 0; // BUTTON3 PORTD.DDR.BIT.B1 = 0; // BUTTON2 PORTD.DDR.BIT.B2 = 0; // BUTTON1 PORTD.DDR.BIT.B3 = 0; // BUTTON0 PORTA.DDR.BIT.B2 = 1; // LED0 PORTA.DDR.BIT.B3 = 1; // LED1 PORTA.DDR.BIT.B4 = 1; // LED2 PORTA.DDR.BIT.B5 = 1; // LED3 }
DDRレジスタにて、入力出力方向を設定します。0: 入力,1: 出力となります。
今回のようにHardwareSetup関数で一度だけ呼ばれる場合はビット単位でアクセスしても問題はありませんが、高速で何度も呼ばれる場合には、レジスタ単位でのアクセスを推奨します。
これでピンの入出力設定が完了したので、BUTTON0~3ボタンの入力に応じて、LEDがそれぞれ点灯するようにmain文にて処理します。
main
#include "iodefine.h" void main(void) { while(1){ if(!PORTD.PORT.BIT.B3) PORTA.DR.BIT.B2 = 1; else PORTA.DR.BIT.B2 = 0; if(!PORTD.PORT.BIT.B2) PORTA.DR.BIT.B3 = 1; else PORTA.DR.BIT.B3 = 0; if(!PORTD.PORT.BIT.B1) PORTA.DR.BIT.B4 = 1; else PORTA.DR.BIT.B4 = 0; if(!PORTD.PORT.BIT.B0) PORTA.DR.BIT.B5 = 1; else PORTA.DR.BIT.B5 = 0; } }
ボタンの入力情報はPORTレジスタに収納されます。
また、出力するためにはDRレジスタを設定します。1: High, 0: Lowを出力となります。
今、ボタンは10kΩの抵抗でプルアップされているため、通常時はHighであり、押されているときはLowになります。
なので、if文条件式にてPORTレジスタの負論理をとり、ボタンがHighのときにLEDの出力はLowになるようにしました。
ただ、if文にてこの操作をするのはあまり効率的とは言えないので、レジスタ間を直接操作するほうが効率的かと思われます。
その際には
#define PORTA.DR.BIT.B2 LED0 #define PORTD.PORT.BIT.B3 BUTTON0
とデファイン等しておくと可読性が上がるかと思います。
20160822の記事
Renesas RX621 クロック
RX621のクロック設定は非常に簡単になっております。
hardware_setup.cにて以下のようにClock_initialzie関数を作成し、HardwareSetup関数にて呼び出します。
hardware_setup.c
#include "iodefine.h" void Clock_initialize(void); void HardwareSetup(void) { Clock_initialize(); } void Clock_initialize(void) { SYSTEM.SCKCR.BIT.ICK = 0x0; // ICLK: XTAL*8 SYSTEM.SCKCR.BIT.PCK = 0x1; // PCLK: XTAL*4 SYSTEM.SCKCR.BIT.BCK = 0x2; // BCLK: XTAL*2 }
となります。
SCKCRレジスタにてICLK、PCLK、BCLKのクロックを入力クロックから何倍PLLするか設定するだけです。
ただし、ビット単位でアクセスする場合はこの順でないと初期化できないので注意!レジスタ単位でアクセスする場合は問題ありません。
Renesas RX621 下準備<プロジェクトファイルの確認>
生成されるプロジェクトファイルは以下の通りになります。
RX631と同様のプロジェクトファイルが自動で生成されます。
embedded-blog.ccwo.net
- dbsct.c(セクションの初期化処理用テーブル)
- hardware_setup.c(ハードウェア初期化ルーチン)
- interrupt_handlers.c(ベクタ関数の定義)
- iodefine.h(レジスタ一覧)
- reset_program.c(リセット直後に呼ばれる関数)
- main.c(プロジェクト名.c)(main関数)
- sbrk.c(ヒープ領域:低水準インターフェースルーチン→通常は不要→消しても問題なし)
- sbrk.h(ヒープ領域:低水準インターフェースルーチン→通常は不要→消しても問題なし)
- stacksct.h(スタックサイズの設定)
- typedefine.h(型定義ヘッダ)
- vect.h(ベクタ関数のヘッダ)
- vector_table.c(固定ベクタテーブル)
となっております。
大きく分けて三種類のファイルが生成されます。
- スタック・ヒープ領域
- 割り込みベクタテーブル
- 実行ファイル
(ハードウェア記述ファイルiodefine.h)
マイコンリセット後に呼ばれる順番として、
- reset_program.c→PowerON_Reset_PC関数
- PowerON_Reset_PC関数内→スタック・ヒープの設定
- PowerON_Reset_PC関数内→HardwareSetup関数(hardware_setup.c)
- PowerON_Reset_PC関数内→main関数
となっています。
なので、main関数が呼ばれる前に、HardwareSetup関数等でハードウェアの初期化を完了し、main関数に実際の処理を書いていく手順となります。
20160822の記事
Renesas RX621 下準備<プロジェクトの作成>
e2 studioとRenesas Flash Programmer(以下RFP)のプロジェクトの作成をし、作成プロジェクトファイルの確認と書き込みを行いたいと思います。
RX631とほぼ同じなので、
e2 studioのプロジェクトの作成はこちらを
embedded-blog.ccwo.net
RFPのプロジェクトの作成はこちらを
embedded-blog.ccwo.net
プロジェクトファイル確認と書き込みはこちらを
embedded-blog.ccwo.net
を参考にしつつさくさく進めたいと思います。
e2 studioのプロジェクトを作成していきます。
ファイル→新規→C Project
・選択したタイプのプロジェクトを作成します。
プロジェクトの種類:
Executable (Renesas) Sample Project
ツールチェイン:
Renesas RXC Toolchain
を選択します。
・ターゲット固有の設定の選択
ターゲットの選択: R5F56218BxDP(今回使用するのはR5F56218BDFP)
上記以外はデフォルト
・コード生成とFITの設定
デフォルト
・追加CPUのオプション
デフォルトを推奨(任意で変更)
・グローバル・オプション設定
デフォルト
・標準ヘッダー・ファイル
ライブラリー構成: C(C99)
任意のヘッダー・ファイルの選択
・各種スタック領域を設定し、サポート・ファイルを追加
ユーザー・スタック・サイズ: 2000
ハードウェア設定関数の生成: C/C++ Source File
これでプロジェクトの作成を「終了」します。プロジェクの要約がホップアップするので「OK」を押します。
e2 studioの設定はこれで終了します。ctrl+Bもしくは左上トンカチマークを押しビルドします。(初回は標準ライブラリーのビルドのため、少々長くなります。)
RFPのプロジェクトを作成していきます。
RFPを開きます。
画像中の青色になっているワークスペースRXで右クリックをし、プロジェクトの追加を選択します。
・新しいワークスペースの作成
使用するターゲット・マイクロコントローラ: グループ→RX 製品名: Generic Boot Device
プロジェクト名: 任意のプロジェクト名
に設定します。
・通信方式
今回はCOMを使用するので、使用ツールはRXが接続されているCOM番号を選びます。
・Genericデバイス問い合わせ
すべてに緑チェックが付いたらOKを押します。
・クロック供給
入力クロック: 12MHz(入力メインクロックを入力します。)
CKP: 4
・通信速度
「デフォルト値を使用する」のチェックを外し、115200bpsを選択します。
設定できましたら、「完了」を選択し、プロジェクトの作成を終了します。
つぎに、RFPで作成したプロジェクトにプログラム・ファイルを追加します。
画像中の青色になっているプロジェクト***で右クリックをし、プログラム・ファイルの追加をします。
~/(e2 studioプロジェクト名)/HardwareDebug/(e2 studioプロジェクト名).mot
を選択し、プログラム・ファイルを追加します。
この状態で一度鉛筆マークもしくは(e2 studioプロジェクト名).motの上で右クリックをし書き込みを選択し書き込みましょう。
(書き込み等に失敗する場合はマイコンを一度リセットし、画像左上緑色ボタンで接続を一度解除し、再接続し書き込みましょう。)
これでe2 studioとRFPのプロジェクト作成が完了しました。
Renesas RX621 はじめに
RX631の初期設定みたいなことをしましたが、RX621のほうもやっていきたいと思います。
RX631に比べれると非常に簡単なので、RX631の記事も参考にしつつさくさくやりたいと思います。
使用するマイコンは秋月電子通商でも購入可能な
RX621マイコン R5F56218BDFP: マイコン関連 秋月電子通商-電子部品・ネット通販
です。秋月電子通商では評価ボードも販売しているので利用するといいかもしれません。
RX621マイコンボード: マイコン関連 秋月電子通商-電子部品・ネット通販
ではさっそく、
準備するものと、やることはRX631とほぼ変わらず、
準備するもの
- e2 studio
- Rnesas Flash Programmer
- RX621データシート
- RX621評価基板等
- C言語、レジスタの知識、MCUの知識
です。
やること
- 下準備<プロジェクの作成>
- 下準備<プロジェクトファイルの確認>
- クロック
- I/O
- UART
- Timer割り込み
- PWM
- MTU2(エンコーダ)
- 12bitADC
- RTC
について書いて行きたいと思います。
それではよろしくお願いします。
20160822の記事
Renesas RX631 余談<1>
ここまででRX631開発は
- プロジェクトの作成
- クロック
- I/O
- UART
- Timer割り込み
- PWM
- エンコーダ
- 12bitADC
と扱えるようになり、 hardware_setup.cのHardwareSeup関数は
void HardwareSetup(void) { Clock_initialize(); Pin_initialize(); SCI1_initialize(); CMT0_initialize(); CMT1_initialize(); MTU2_initialize(); S12AD_initialize(); }
このようになっていることと思います。基本的にはモジュール(周辺機能)単位でinitialize関数を作っています。また、ユーザ関数や割り込み等が必要な場合には別でファイルを作っています。
これで、あらかたの機能は使えるようになったので、これから「マイコンで何かをしよう!」の下地には最低限なっているのかな~っと思っています。
これ以外の機能が必要になった場合でも、手順はほとんど変わることはないかと思います。
1.消費電力低減機能の解除
→ほぼ必ず必要。
2.ピンの設定
→ピンとともに使う周辺機能であれば必ず必要。
3.周辺機能の設定
→必須。
4.ユーザ関数の設定
→割り込み関数、周辺機能を使いやすくするための関数(例えば、PWMを%から変換して出力や、エンコーダを回転数に変換等)、このほかにも今回は扱わなかったCAN、SPI、I2C等では通信の関数が必ず必要となります。
自分も何度も壁にぶちあたったりしていますが、気負わずに、しっかりデータシートや当ブログのようなサイトを参考にゆっくり、確実に、精度よくやっていくことがいいかと思います。
番外編として
・RX631,621の最小構成回路
を予定しています。(通信もできたら・・・)
Renesas RX631 12bitADC
アナログ電圧の計測等で必要不可欠な12bitADCの設定を行います。
12bitADC=12bit Analog Digital Converterの略でアナログ電圧を12bitのディジタル信号に変換することを指します。
これにより、アナログ電圧をディジタル信号つまりビットに変換し、取り扱うことができ、アナログ電圧を出力するセンサー等の値をマイコンにより処理することでき大変便利です。
RX631(R5F5631FDDFP)では12bitAD変換が全14chあるのですべてを動作させたいと思います。
以下の手順で設定していきます。
1.消費電力低減機能の解除
2.ピンの設定
3.S12ADの設定
4.ユーザ関数の定義
はじめに
1.消費電力低減機能の解除
2.ピンの設定
を一度にみていきましょう。
まず、S12AD.cとS12AD.hという名前で次のファイルをプロジェクトに追加します。
e2 stduio左側プロジェクトエクスプローラのプロジェクトフォルダ内のsrcフォルダを右クリックし,
新規→ソース・ファイルからS12AD.c
新規→ヘッダ・ファイルからS12AD.h
を作成します。
ソースコードの追加はプロジェクトファイルのsrcフォルダに直接作成することでも可能です。
つぎに、hardware_setup.cにてS12AD.hをインクルードし、HardwareSetup関数でS12AD.cのS12AD_initialize関数を呼びます。
S12AD.h
#ifndef S12AD_H_ #define S12AD_H_ extern unsigned short ADC12[14]; void S12AD_initialize(void); #endif /* S12AD_H_ */
S12AD.c
#include "iodefine.h" #include "machine.h" #include "vect.h" #include "S12AD.h" #pragma interrupt (S12AD_interrupt(vect=VECT(S12AD, S12ADI0))) unsigned short ADC12[14] = {0}; void s12ad_initialize(void){ SYSTEM.PRCR.WORD = 0xA502; // Release Protect MSTP(S12AD) = 0; // Wake up S12AD SYSTEM.PRCR.WORD = 0xA500; // Protect // Set MPC PORT4.PMR.BIT.B0 = 1; // Set P40-7: Peripheral PORT4.PMR.BIT.B1 = 1; PORT4.PMR.BIT.B2 = 1; PORT4.PMR.BIT.B3 = 1; PORT4.PMR.BIT.B4 = 1; PORT4.PMR.BIT.B5 = 1; PORT4.PMR.BIT.B6 = 1; PORT4.PMR.BIT.B7 = 1; PORTD.PMR.BIT.B0 = 1; // Set PD0-5: Peripheral PORTD.PMR.BIT.B1 = 1; PORTD.PMR.BIT.B2 = 1; PORTD.PMR.BIT.B3 = 1; PORTD.PMR.BIT.B4 = 1; PORTD.PMR.BIT.B5 = 1; MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.P40PFS.BIT.ASEL = 1; // Set P40-7: Analog Input MPC.P41PFS.BIT.ASEL = 1; MPC.P42PFS.BIT.ASEL = 1; MPC.P43PFS.BIT.ASEL = 1; MPC.P44PFS.BIT.ASEL = 1; MPC.P45PFS.BIT.ASEL = 1; MPC.P46PFS.BIT.ASEL = 1; MPC.P47PFS.BIT.ASEL = 1; MPC.PD0PFS.BIT.ASEL = 1; // Set PD0-5: Analog Input MPC.PD1PFS.BIT.ASEL = 1; MPC.PD2PFS.BIT.ASEL = 1; MPC.PD3PFS.BIT.ASEL = 1; MPC.PD4PFS.BIT.ASEL = 1; MPC.PD5PFS.BIT.ASEL = 1; MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1; // S12AD Settings software trigger S12AD.ADCSR.BIT.CKS = 0; // PCLK/8 S12AD.ADCSR.BIT.ADIE = 1; // Enable S12ADI0 S12AD.ADCSR.BIT.ADCS = 1; // Continuous scanning S12AD.ADCSR.BIT.ADST = 0; // Stop S12AD S12AD.ADANS0.WORD = 0xFFFF; // S12ADC: AN000-015 S12AD.ADADS0.WORD = 0x0000; // Disable ADD result Mode S12AD.ADCER.BIT.ACE = 1; // Auto clearing S12AD.ADCER.BIT.ADRFMT = 0; // Right -justified S12AD.ADSSTR01.BIT.SST1 = 60; // 10us S12AD.ADCSR.BIT.ADST = 1; // Enable S12AD IEN(S12AD, S12ADI0) = 1; IPR(S12AD, S12ADI0) = 8; } void S12AD_interrupt(void){ ADC12[0] = S12AD.ADDR0; ADC12[1] = S12AD.ADDR1; ADC12[2] = S12AD.ADDR2; ADC12[3] = S12AD.ADDR3; ADC12[4] = S12AD.ADDR4; ADC12[5] = S12AD.ADDR5; ADC12[6] = S12AD.ADDR6; ADC12[7] = S12AD.ADDR7; ADC12[8] = S12AD.ADDR8; ADC12[9] = S12AD.ADDR9; ADC12[10] = S12AD.ADDR10; ADC12[11] = S12AD.ADDR11; ADC12[12] = S12AD.ADDR12; ADC12[13] = S12AD.ADDR13; }
まず、消費電力低減機能の解除を見ていきます。
SYSTEM.PRCR.WORD = 0xA502; // Release Protect MSTP(S12AD) = 0; // Wake up S12AD SYSTEM.PRCR.WORD = 0xA500; // Protect
これまでと同様に、消費電力低減機能のアクセスロックを解除し、消費電力低減機能を解除し、再度ロックします。
つぎにピンの設定を見てきましょう。
// Set MPC PORT4.PMR.BIT.B0 = 1; // Set P40-7: Peripheral PORT4.PMR.BIT.B1 = 1; PORT4.PMR.BIT.B2 = 1; PORT4.PMR.BIT.B3 = 1; PORT4.PMR.BIT.B4 = 1; PORT4.PMR.BIT.B5 = 1; PORT4.PMR.BIT.B6 = 1; PORT4.PMR.BIT.B7 = 1; PORTD.PMR.BIT.B0 = 1; // Set PD0-5: Peripheral PORTD.PMR.BIT.B1 = 1; PORTD.PMR.BIT.B2 = 1; PORTD.PMR.BIT.B3 = 1; PORTD.PMR.BIT.B4 = 1; PORTD.PMR.BIT.B5 = 1; MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.P40PFS.BIT.ASEL = 1; // Set P40-7: Analog Input MPC.P41PFS.BIT.ASEL = 1; MPC.P42PFS.BIT.ASEL = 1; MPC.P43PFS.BIT.ASEL = 1; MPC.P44PFS.BIT.ASEL = 1; MPC.P45PFS.BIT.ASEL = 1; MPC.P46PFS.BIT.ASEL = 1; MPC.P47PFS.BIT.ASEL = 1; MPC.PD0PFS.BIT.ASEL = 1; // Set PD0-5: Analog Input MPC.PD1PFS.BIT.ASEL = 1; MPC.PD2PFS.BIT.ASEL = 1; MPC.PD3PFS.BIT.ASEL = 1; MPC.PD4PFS.BIT.ASEL = 1; MPC.PD5PFS.BIT.ASEL = 1; MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1;
SCI同様にMPCのアクセスロックを解除し、ピンに周辺機能を割り当て、再度ロックし、下準備を完了します。
今回は14個のピンを一度に設定するのでかなり長くなっています。個別に設定しているのはあとで変更しやすくするためです。
固定であるならば、一括でレジスタを設定してもよいでしょう。
3.S12ADの設定
S12ADはデータシートに初期化手順が示されているわけではないので、今回は連続スキャンモードを参考に設定していきます。
しかし、ここで余談ですが、RXに限らずマイコンのAD変換の設定はおおまかに
・AD変換クロックの設定(モジュールの動作周波数の設定)
・トリガ、割り込みの設定(AD変換のトリガの設定)
・AD変換chの設定(何個AD変換を使用するの設定)
・加算、フィルタの設定(精度を上げたりするための設定)
・データ型の設定(右詰め、左詰め(Little,Big Endian=Byte-order)の設定)
・サンプリング周期の設定(AD変換には一般的にある程度の時間を要さないと構造的に精度が出ないためサンプリング時間の設定をする。)
RX631もほぼ例に漏れることなく設定していく。
// S12AD Settings software trigger // AD変換クロックの設定 S12AD.ADCSR.BIT.CKS = 0; // PCLK/8 // 割り込みの設定 S12AD.ADCSR.BIT.ADIE = 1; // Enable S12ADI0 S12AD.ADCSR.BIT.ADCS = 1; // Continuous scanning=連続スキャンモード S12AD.ADCSR.BIT.ADST = 0; // Stop S12AD=AD変換の設定をするため一旦モジュールをストップ // AD変換chの設定 S12AD.ADANS0.WORD = 0xFFFF; // S12ADC: AN000-015 // 加算、フィルタの設定 S12AD.ADADS0.WORD = 0x0000; // Disable ADD result Mode S12AD.ADCER.BIT.ACE = 1; // Auto clearing // データ型の設定 S12AD.ADCER.BIT.ADRFMT = 0; // Right -justified // サンプリング周期の設定 S12AD.ADSSTR01.BIT.SST1 = 60; // 10us S12AD.ADCSR.BIT.ADST = 1; // Enable S12AD=AD変換開始
このように、一般的なマイコンと同様(かなり高機能な部類)にして設定が可能である。
最後に、タイマー割り込みで設定したときと同様に割り込みの設定をする。
IEN(S12AD, S12ADI0) = 1; IPR(S12AD, S12ADI0) = 8;
同様にvect.hの
// S12AD S12ADI0 #pragma interrupt (Excep_S12AD_S12ADI0(vect=102)) void Excep_S12AD_S12ADI0(void);
をすべてコメントアウトします。
これでAD変換の設定は完了しました。
4.ユーザ関数の定義
割り込みベクタテーブルの設定と関数の設定をします。
#include "vect.h" #pragma interrupt (S12AD_interrupt(vect=VECT(S12AD, S12ADI0))) void S12AD_interrupt(void){ ADC12[0] = S12AD.ADDR0; ADC12[1] = S12AD.ADDR1; ADC12[2] = S12AD.ADDR2; ADC12[3] = S12AD.ADDR3; ADC12[4] = S12AD.ADDR4; ADC12[5] = S12AD.ADDR5; ADC12[6] = S12AD.ADDR6; ADC12[7] = S12AD.ADDR7; ADC12[8] = S12AD.ADDR8; ADC12[9] = S12AD.ADDR9; ADC12[10] = S12AD.ADDR10; ADC12[11] = S12AD.ADDR11; ADC12[12] = S12AD.ADDR12; ADC12[13] = S12AD.ADDR13; }
上記はタイマー割り込みと設定したときと同様で、S12AD_interrupt関数をベクタテーブル番号VECT(S12AD,S12ADI0)で呼ばれるようにpragmaにて定義し、
割り込み処理関数ではグローバル変数で用意したADC12配列にひとつずつ収納していく形になっています。
これで設定は完了しました。
最後にmain文にて
#include <stdio.h> #include "iodefine.h" #include "machine.h" #include "S12AD.h" void main(void) { int i; while(1){ for(i = 0; i < 14; i++){ printf("S12AD[%d]:%4d\n", i, ADC12[i]); }; } }
という感じでデバッグしてみましょう。TeraTerm等ではおそらくランダムな値が出てくると思います。(Hi-Z状態のため、値が震えてる。)
これで12bitADCは完了です!
20160818の記事
Renesas RX631 エンコーダ
モータ制御等に必要不可欠なエンコーダを計測していきたいと思います。
エンコーダとは、光とスリットによって、回転方向と回転量を計測することができるセンサーです。
広くモータの回転量の制御や回転量の変化を利用し移動量の計測に使用されます。
以下の手順で設定していきます。エンコーダはMTU2(マルチファンクションタイマパルスユニット2)を用います。
1.消費電力低減機能の解除
2.ピンの設定
3.MTU2の設定
4.ユーザ関数の設定(今回、関数は定義しないので省略)
はじめに
1.消費電力低減機能の解除
2.ピンの設定
を一度にみていきましょう。
PWMの設定時に作成したMTU2_initialize関数に続けて位相係数の設定をしていきます。
MTU2_initialize関数
void MTU2_initialize(void){ // MTU1,MTU2 SYSTEM.PRCR.WORD = 0xA502; // Release Protect MSTP(MTU1) = 0; // Wake up MTU1 MSTP(MTU2) = 0; // Wake up MTU2 SYSTEM.PRCR.WORD = 0xA500; // Protect // Set MPC PORT1.PMR.BIT.B4 = 1; // Set P14: Peripheral PORT1.PMR.BIT.B5 = 1; // Set P15: Peripheral PORTC.PMR.BIT.B4 = 1; // Set PC4: Peripheral PORTC.PMR.BIT.B5 = 1; // Set PC5: Peripheral MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.P14PFS.BIT.PSEL = 2; // Set P14: MTCLKA MPC.P15PFS.BIT.PSEL = 2; // Set P15: MTCLKB MPC.PC4PFS.BIT.PSEL = 2; // Set PC4: MTCLKC MPC.PC4PFS.BIT.PSEL = 2; // Set PC5: MTCLKD MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1; // MTU1, MTU2 Encoder Settings 位相係数モード1 MTU1.TMDR.BIT.MD = 4; // MTU1 位相係数モード1 MTU2.TMDR.BIT.MD = 4; // MTU2 位相係数モード1 MTU1.TCNT = 0x0000; // Clear MTU1 count MTU2.TCNT = 0x0000; // Clear MTU2 count MTU.TSTR.BIT.CST1 = 1; // Start MTU1 count MTU.TSTR.BIT.CST2 = 1; // Start MTU2 count // MTU4(設定省略) }
まず、SCI、Timer割り込み同様に、消費電力低減機能のアクセスロックを解除し、消費電力低減機能を解除し、再度ロックします。
つぎに、SCI同様にMPCのアクセスロックを解除し、ピンに周辺機能を割り当て、再度ロックし、下準備を完了します。
3.MTU2の設定
MTU2の設定をします。
データシートp.837の図23.29を参考に設定します。
ここで、MTU2の中には、MTU0~6のユニットがあり、今回は位相係数モードが使用できるMTU1,2を用います。
まず、MTU1とMTU2を位相係数モード1に設定し、MTUのカウントをクリアし、MTUのカウントをスタートさせます。
これでMTUの設定は完了です!
また、4.ユーザ関数の定義ですが今回関数等は設定しないので省略します。
次にmain文にて
#include <stdio.h> #include "iodefine.h" #include "machine.h" void main(void) { while(1){ printf("MTU1:%6d\tMTU2:%6d\n", MTU1.TCNT, MTU2.TCNT); } }
エンコーダを接続し、TeraTerm等でデバッグできれば成功です!
エンコーダの値が収納されるTCNTレジスタは16ビットのレジスタであることには頭に入れておきましょう。
ここで、可読性をあげるために、
#define MTU1.TCNT ENCODER0 #define MTU2.TCNT ENCODER1
としておくのもよいでしょう。
20160818の記事
Renesas RX631 PWM
モーター制御等さまざまな分野で必要不可欠なPWMを出力していきます。
PE2をMTIOC4Aに設定し、1kHzのPWMを出力していきます。
以下の手順で設定していきます。
1.消費電力低減機能の解除
2.ピンの設定
3.MTU2の設定
4.ユーザ関数の定義(今回、関数は定義しないので省略)
はじめに
1.消費電力低減機能の解除
2.ピンの設定
を一度にみていきましょう。
hardware_setup.cにて以下のMTU2_initialize関数を作成し、HardwareSetup関数で呼びましょう。
MTU2_initialize関数
void MTU2_initialize(void){ // MTU4 SYSTEM.PRCR.WORD = 0xA502; // Release Protect MSTP(MTU4) = 0; // Wake up MTU4 SYSTEM.PRCR.WORD = 0xA500; // Protect // Set MPC PORTE.PMR.BIT.B2 = 1; // Set PE2: Peripheral MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.PE2PFS.BIT.PSEL = 1; // Set PE2: MTIOC4A MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1; // PWM Settings MTU.TOER.BIT.OE4A = 1; // Enable MTIOC4A Output MTU4.TCR.BIT.TPSC = 4; // PCLK/256 MTU4.TCR.BIT.CKEG = 0; // Count rising edge MTU4.TCR.BIT.CCLR = 1; // Clear TCNT count when compare match TGRA MTU4.TMDR.BIT.MD = 2; // MTU4 PWMモード1 MTU4.TMDR.BIT.BFA = 0; // TGRA, TGRC normal mode MTU4.TMDR.BIT.BFB = 0; // TGRB, TGRD normal mode MTU4.TIORH.BIT.IOA = 6; // Compare Output High MTU4.TIORH.BIT.IOB = 7; // Compare Output Low MTU4.TGRA = 29999; // 1kHz MTU4.TGRB = 14999; // Duty 50% MTU4.TCNT = 0; // Clear MTU4 count MTU.TSTR.BIT.CST4 = 1; // Start MTU4 count }
まず、SCI、Timer割り込み同様に、消費電力低減機能のアクセスロックを解除し、消費電力低減機能を解除し、再度ロックします。
つぎに、SCI同様にMPCのアクセスロックを解除し、ピンに周辺機能を割り当て、再度ロックし、下準備を完了します。
3.MTU2の設定
データシートp.834の図23.25と図23.26を参考に設定します。
ここで、MTU2の中にはMTU0~6のユニットがあり、今回はMTU4をPWMモード1で使用します。
まず、PWMモードの設定手順例を参考に設定していきます。
・カウンタクロックの設定
→使用するクロックの分周比を設定します。
・カウンタクリアの要因の選択
→カウンタクロックがクリアされるソースを選択します。TGRAまたはTGRB。
・PWMモードの設定
→PWMモード1に設定します。
・波形出力レベルの選択
→どのように波形を出すか設定します。今回は初期HIGHに設定し、TGRBのコンペアマッチでLOW出力に、TGRAのコンペアマッチでHIGH出力。
・TGRの設定
→実際に値を設定し、PWM周波数とDuty比を設定します。今回はTGRB操作することでDuty比、TGRAを操作することでPWM周波数を設定します。ただし常にTGRA>TGRBを満たす。
・カウント動作開始
→実際にカウントスタートさせ動作を開始します。
(ただし、今回MTIOC4Aは初期状態では出力が禁止されているため、TOERレジスタのOE4Aビットを1にし、出力を許可しておきます。)
4.ユーザ関数の定義
今回は用意しないため省略します。
これでPWMの設定は完了しました。実際に書き込みオシロスコープ等でPE2の波形を観測し、PWMが出ていれば成功です!
PWM波形の一例を載せておきます。
20160818の記事
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の設定
データシート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の記事
Renesas RX631 UART
UARTを用いて、RX631のデバッグをします。
回路はRX631→SCI1→FT231XS→COM(パソコン)となります。
ここでは、printf(IOStream)を使うために、RenesasのSIM I/O(シミュレーション用のデバッグコンソール)を用いて、PCにシリアル転送を行います。
(自身でprintf関数を実装するのもいいかと思います。)
まず、lowsrc.cとlowsrc.hという名前で次のファイルをプロジェクトに追加します。(ソースコードは長いので後述。)(CS+のプロジェクトファイルから転用しました。)
e2 stduio左側プロジェクトエクスプローラのプロジェクトフォルダ内のsrcフォルダを右クリックし,
新規→ソース・ファイルからlowsrc.c
新規→ヘッダ・ファイルからlowsrc.h
を作成します。(ソースコードは長いので後述。)
ソースコードの追加はプロジェクトファイルのsrcフォルダに直接作成することでも可能です。
次に、reset_program.c内の
//#ifdef __cplusplus // Use SIM I/O //extern "C" { //#endif //extern void _INIT_IOLIB(void); //extern void _CLOSEALL(void); //#ifdef __cplusplus //} //#endif
とreset_program.cのPowerON_Reset_PC関数内の
//_INIT_IOLIB(); //_CLOSEALL();
のコメントアウトを外します。
つぎに、SCI1(UART)の設定をします。
ソースコードを追加するために、e2 stduio左側プロジェクトエクスプローラのプロジェクトフォルダ内のsrcフォルダを右クリックし,
新規→ソース・ファイルからSCI.c
新規→ヘッダ・ファイルからSCI.h
を作成します。(ソースコードは長いので後述。)
つぎに、hardware_setup.cにてSCI.hをインクルードし、HardwareSetup関数でSCI.cのSCI1_initialize関数を呼びます。
これで、デバッグの準備は出来ました!
main文にて
#include <stdio.h> #include "iodefine.h" #include "machine.h" void main(void) { unsigned int i; while(1){ printf("Hello RX World!\n"); for(i = 0; i < 65000; i++) nop(); } }
TeraTerm等を開き、COMを接続し、Hello RX World!が表示されれば成功です!連続でprintfをするのは処理が重いため、nop関数を用いてdelayをとっています。
これでターミナル等を用いてデバッグをすることが可能になりました。
COMの設定は
・ボーレート:115200
・データ:8bit
・パリティ:なし
・ストップビット:1bit
と一般的な設定になっています。
大まか設定としては、SCI1を使ってprintfにリダイレクト(charput,charget)しているということになるかとおもいます。
SCIの初期化の手順は
1.消費電力低減機能の解除
2.ピン設定
3.SCIの初期化
4.ユーザー関数の定義
の順でおこいます。
1.消費電力低減機能の解除
RX631シリーズには、RX621シリーズには実装されていなかった、消費電力低減機能が実装されています。
これはレジスタの初期状態ではすべての周辺機能が消費電力低減状態で起動しており、これを解除するまで任意の周辺機能を使用することが出来ません。
なので、まずはじめにSCI1の消費電力低減機能を解除します。
// SCI1 SYSTEM.PRCR.WORD = 0xA502; // Release protect MSTP(SCI1) = 0; // Wake up SCI1 SYSTEM.PRCR.WORD = 0xA500; // Protect
となります。MSTPCRレジスタに各モジュールの消費電力低減機能を解除するビットがあり、このビットがMSTP(***)というふうにiodefine.hでマクロされているので、これを使用します。
しかし、このMSTPCRレジスタにはアクセスロックがかかっているため、PRCRレジスタに0xA502を設定し、ロックを解除し、消費電力低減機能を解除した後は、再度ロックします。
2.ピン設定
// Set MPC MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.P26PFS.BIT.PSEL = 0x0A; // Set P26: TXD1 MPC.P30PFS.BIT.PSEL = 0x0A; // Set P30: RXD1 MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1; PORT2.PMR.BIT.B6 = 1; // P26: peripheral PORT3.PMR.BIT.B0 = 1; // P30: peripheral
次に、ピン設定をします。P26,P30をTXD1,RXD1に設定します。
MPC(マルチピンファンクション)レジスタにて各ピンに任意の周辺機能を付与することができます。
しかし、MPCレジスタはアクセスロックがかかっているため、PWPRレジスタを上コードのように設定し、アクセスロックを解除し、ピンに周辺機能の設定をし、再度ロックをします。
その後、PMR(ポートモードレジスタ)レジスタにてIOピンから周辺機能にピンの状態を変更し、ピンの設定を完了します。
3.SCIの初期化
RX63Nグループ、RX631グループ ユーザーズマニュアル ハードウェア編 Rev1.80(以下データシート)のp.1414の図35.8からSCIの初期化フローチャートの例(調歩同期式モード)を参考に、初期設定を行います。
// SCI1 Settings SCI1.SCR.BIT.TEIE = 0; // Disable TEIE SCI1.SCR.BIT.MPIE = 0; // Normal Mode SCI1.SCR.BIT.RE = 0; // Disable RX SCI1.SCR.BIT.TE = 0; // Disable TX SCI1.SCR.BIT.RIE = 0; // Disable RXI, ERI SCI1.SCR.BIT.TIE = 0; // Disable TXI SCI1.SCR.BIT.CKE = 0; // built in baud rate SCI1.SMR.BIT.CKS = 0; // PCLK SCI1.SMR.BIT.MP= 0; // Disable multiple processor SCI1.SMR.BIT.STOP = 0; // 1 stop bit SCI1.SMR.BIT.PM = 0; // none parity SCI1.SMR.BIT.CHR = 0; // 8bit data length SCI1.SMR.BIT.CM = 0; // 調歩同期式 SCI1.BRR = 12; // 115200bps SCI1.SCR.BIT.RE = 1; // Enable RX SCI1.SCR.BIT.TE = 1; // Enable TX
ここはデータシート通りかと思います。
4.ユーザー関数の定義
ここではprintfでリダイレクされてくる、cahrputとchargetを定義しておきます。
これでSCIの初期設定は完了です。
以下使用ソースコード
SCI.h
#ifndef SCI_H_ #define SCI_H_ void SCI1_initialize(void); #endif /* SCI_H_ */
SCI.c
#include "iodefine.h" #include "machine.h" #include "SCI.h" void SCI1_initialize(void){ // SCI1 SYSTEM.PRCR.WORD = 0xA502; // Release protect MSTP(SCI1) = 0; // Wake up SCI1 SYSTEM.PRCR.WORD = 0xA500; // Protect // Set MPC MPC.PWPR.BIT.B0WI = 0; // Release protect MPC.PWPR.BIT.PFSWE = 1; MPC.P26PFS.BIT.PSEL = 0x0A; // Set P26: TXD1 MPC.P30PFS.BIT.PSEL = 0x0A; // Set P30: RXD1 MPC.PWPR.BIT.PFSWE = 0; // Protect MPC.PWPR.BIT.B0WI = 1; PORT2.PMR.BIT.B6 = 1; // P26: peripheral PORT3.PMR.BIT.B0 = 1; // P30: peripheral // SCI1 Settings SCI1.SCR.BIT.TEIE = 0; // Disable TEIE SCI1.SCR.BIT.MPIE = 0; // Normal Mode SCI1.SCR.BIT.RE = 0; // Disable RX SCI1.SCR.BIT.TE = 0; // Disable TX SCI1.SCR.BIT.RIE = 0; // Disable RXI, ERI SCI1.SCR.BIT.TIE = 0; // Disable TXI SCI1.SCR.BIT.CKE = 0; // built in baud rate SCI1.SMR.BIT.CKS = 0; // PCLK SCI1.SMR.BIT.MP= 0; // Disable multiple processor SCI1.SMR.BIT.STOP = 0; // 1 stop bit SCI1.SMR.BIT.PM = 0; // none parity SCI1.SMR.BIT.CHR = 0; // 8bit data length SCI1.SMR.BIT.CM = 0; // 調歩同期式 SCI1.BRR = 12; // 115200bps SCI1.SCR.BIT.RE = 1; // Enable RX SCI1.SCR.BIT.TE = 1; // Enable TX } void charput(char c) { while(SCI1.SSR.BIT.TEND == 0); SCI1.TDR = c; SCI1.SSR.BIT.TEND = 0; } char charget(void){ char data; data = SCI1.RDR; ICU.IR[VECT_SCI1_RXI1].BIT.IR = 0; return data; }
lowsrc.h
/*Number of I/O Stream*/ #define IOSTREAM 3
lowsrc.c
#include <string.h> #include <stdio.h> #include <stddef.h> #include "lowsrc.h" /* file number */ #define STDIN 0 /* Standard input (console) */ #define STDOUT 1 /* Standard output (console) */ #define STDERR 2 /* Standard error output (console) */ #define FLMIN 0 /* Minimum file number */ #define _MOPENR 0x1 #define _MOPENW 0x2 #define _MOPENA 0x4 #define _MTRUNC 0x8 #define _MCREAT 0x10 #define _MBIN 0x20 #define _MEXCL 0x40 #define _MALBUF 0x40 #define _MALFIL 0x80 #define _MEOF 0x100 #define _MERR 0x200 #define _MLBF 0x400 #define _MNBF 0x800 #define _MREAD 0x1000 #define _MWRITE 0x2000 #define _MBYTE 0x4000 #define _MWIDE 0x8000 /* File Flags */ #define O_RDONLY 0x0001 /* Read only */ #define O_WRONLY 0x0002 /* Write only */ #define O_RDWR 0x0004 /* Both read and Write */ #define O_CREAT 0x0008 /* A file is created if it is not existed */ #define O_TRUNC 0x0010 /* The file size is changed to 0 if it is existed. */ #define O_APPEND 0x0020 /* The position is set for next reading/writing */ /* 0: Top of the file 1: End of file */ /* Special character code */ #define CR 0x0d /* Carriage return */ #define LF 0x0a /* Line feed */ extern const long _nfiles; /* The number of files for input/output files */ char flmod[IOSTREAM]; /* The location for the mode of opened file. */ unsigned char sml_buf[IOSTREAM]; #define FPATH_STDIN "C:\\stdin" #define FPATH_STDOUT "C:\\stdout" #define FPATH_STDERR "C:\\stderr" /* Output one character to standard output */ extern void charput(unsigned char); /* Input one character from standard input */ extern unsigned char charget(void); #include <stdio.h> FILE *_Files[IOSTREAM]; // structure for FILE char *env_list[] = { // Array for environment variables(**environ) "ENV1=temp01", "ENV2=temp02", "ENV9=end", '\0' // Terminal for environment variables }; char **environ = env_list; /****************************************************************************/ /* _INIT_IOLIB */ /* Initialize C library Functions, if necessary. */ /* Define USES_SIMIO on Assembler Option. */ /****************************************************************************/ void _INIT_IOLIB( void ) { /* A file for standard input/output is opened or created. Each FILE */ /* structure members are initialized by the library. Each _Buf member */ /* in it is re-set the end of buffer pointer. */ /* Initializations of File Stream Table */ _Files[0] = stdin; _Files[1] = stdout; _Files[2] = stderr; /* Standard Input File */ if( freopen( FPATH_STDIN, "r", stdin ) == NULL ) stdin->_Mode = 0xffff; /* Not allow the access if it fails to open */ stdin->_Mode = _MOPENR; /* Read only attribute */ stdin->_Mode |= _MNBF; /* Non-buffering for data */ stdin->_Bend = stdin->_Buf + 1; /* Re-set pointer to the end of buffer */ /* Standard Output File */ if( freopen( FPATH_STDOUT, "w", stdout ) == NULL ) stdout->_Mode = 0xffff; /* Not allow the access if it fails to open */ stdout->_Mode |= _MNBF; /* Non-buffering for data */ stdout->_Bend = stdout->_Buf + 1;/* Re-set pointer to the end of buffer */ /* Standard Error File */ if( freopen( FPATH_STDERR, "w", stderr ) == NULL ) stderr->_Mode = 0xffff; /* Not allow the access if it fails to open */ stderr->_Mode |= _MNBF; /* Non-buffering for data */ stderr->_Bend = stderr->_Buf + 1;/* Re-set pointer to the end of buffer */ } /****************************************************************************/ /* _CLOSEALL */ /****************************************************************************/ void _CLOSEALL( void ) { long i; for( i=0; i < _nfiles; i++ ) { /* Checks if the file is opened or not */ if( _Files[i]->_Mode & (_MOPENR | _MOPENW | _MOPENA ) ) fclose( _Files[i] ); /* Closes the file */ } } /**************************************************************************/ /* open:file open */ /* Return value:File number (Pass) */ /* -1 (Failure) */ /**************************************************************************/ long open(const char *name, /* File name */ long mode, /* Open mode */ long flg) /* Open flag */ { if( strcmp( name, FPATH_STDIN ) == 0 ) /* Standard Input file? */ { if( ( mode & O_RDONLY ) == 0 ) return -1; flmod[STDIN] = mode; return STDIN; } else if( strcmp( name, FPATH_STDOUT ) == 0 )/* Standard Output file? */ { if( ( mode & O_WRONLY ) == 0 ) return -1; flmod[STDOUT] = mode; return STDOUT; } else if(strcmp(name, FPATH_STDERR ) == 0 ) /* Standard Error file? */ { if( ( mode & O_WRONLY ) == 0 ) return -1; flmod[STDERR] = mode; return STDERR; } else return -1; /*Others */ } long close( long fileno ) { return 1; } /**************************************************************************/ /* write:Data write */ /* Return value:Number of write characters (Pass) */ /* -1 (Failure) */ /**************************************************************************/ long write(long fileno, /* File number */ const unsigned char *buf, /* The address of destination buffer */ long count) /* The number of chacter to write */ { long i; /* A variable for counter */ unsigned char c; /* An output character */ /* Checking the mode of file , output each character */ /* Checking the attribute for Write-Only, Read-Only or Read-Write */ if(flmod[fileno]&O_WRONLY || flmod[fileno]&O_RDWR) { if( fileno == STDIN ) return -1; /* Standard Input */ else if( (fileno == STDOUT) || (fileno == STDERR) ) /* Standard Error/output */ { for( i = count; i > 0; --i ) { c = *buf++; charput(c); } return count; /*Return the number of written characters */ } else return -1; /* Incorrect file number */ } else return -1; /* An error */ } long read( long fileno, unsigned char *buf, long count ) { long i; /* Checking the file mode with the file number, each character is input and stored the buffer */ if((flmod[fileno]&_MOPENR) || (flmod[fileno]&O_RDWR)){ for(i = count; i > 0; i--){ *buf = charget(); if(*buf==CR){ /* Replace the new line character */ *buf = LF; } buf++; } return count; } else { return -1; } } long lseek( long fileno, long offset, long base ) { return -1L; } #ifdef _REENTRANT // For Reentrant Library (generated lbgrx with -reent option) #define MALLOC_SEM 1 /* Semaphore No. for malloc */ #define STRTOK_SEM 2 /* Semaphore No. for strtok */ #define FILE_TBL_SEM 3 /* Semaphore No. for fopen */ #define MBRLEN_SEM 4 /* Semaphore No. for mbrlen */ #define FPSWREG_SEM 5 /* Semaphore No. for FPSW register */ #define FILES_SEM 6 /* Semaphore No. for _Files */ #define SEMSIZE 26 /* FILES_SEM + _nfiles (assumed _nfiles=20) */ #define TRUE 1 #define FALSE 0 #define OK 1 #define NG 0 extern long *errno_addr(void); extern long wait_sem(long); extern long signal_sem(long); static long sem_errno; static int force_fail_signal_sem = FALSE; static int semaphore[SEMSIZE]; /******************************************************************************/ /* errno_addr: Acquisition of errno address */ /* Return value: errno address */ /******************************************************************************/ long *errno_addr(void) { /* Return the errno address of the current task */ return (&sem_errno); } /******************************************************************************/ /* wait_sem: Defines the specified numbers of semaphores */ /* Return value: OK(=1) (Normal) */ /* NG(=0) (Error) */ /******************************************************************************/ long wait_sem(long semnum) /* Semaphore ID */ { if((0 < semnum) && (semnum < SEMSIZE)) { if(semaphore[semnum] == FALSE) { semaphore[semnum] = TRUE; return(OK); } } return(NG); } /******************************************************************************/ /* signal_sem: Releases the specified numbers of semaphores */ /* Return value: OK(=1) (Normal) */ /* NG(=0) (Error) */ /******************************************************************************/ long signal_sem(long semnum) /* Semaphore ID */ { if(!force_fail_signal_sem) { if((0 <= semnum) && (semnum < SEMSIZE)) { if( semaphore[semnum] == TRUE ) { semaphore[semnum] = FALSE; return(OK); } } } return(NG); } #endif
20160818の記事
Renesas RX631 I/O
つぎにI/Oの設定をしていきたいと思います。
(先にクロックの設定http://ccwo.hatenablog.jp/entry/2016/08/18/012106を行って下さい。)
I/O(Input/Output)とは、マイコンの入出力ピンのことを指します。
まずは出力設定をします。ピンよりHighを出力し、LEDを点灯させたいと思います。
Highを出力するピンは、PE1,PE0,PD7,PD6の4つを用います。
回路図は上図のようになります。電流制限抵抗に1kΩを用いてLEDを駆動します。
ではこの4つのピンを設定していきましょう。
まず、hardware_setup.cにPin_initializeという関数を作り、以下のようにレジスタを設定します。
void Pin_initialize(void){ PORTE.PDR.BIT.B1 = 1; // LED0 PORTE.PDR.BIT.B0 = 1; // LED1 PORTD.PDR.BIT.B7 = 1; // LED2 PORTD.PDR.BIT.B6 = 1; // LED3 }
PORTレジスタのPDRを用いてピンを個別に出力設定に変更します。
今回のように一度しか呼ばないような場合はビット単位でレジスタにアクセスしても問題はないかと思いますが、高速で複数回呼ばれるような場合はレジスタ単位でレジスタにアクセスしましょう。
そして、この関数をHardwareSetup関数内で呼びます。
void HardwareSetup(void) { Clock_initialize(); Pin_initialize(); }
続いてメイン文にて、
#include "iodefine.h" void main(void) { PORTE.PODR.BIT.B1 = 1; // LED0 PORTE.PODR.BIT.B0 = 1; // LED1 PORTD.PODR.BIT.B7 = 1; // LED2 PORTD.PODR.BIT.B6 = 1; // LED3 }
これで出力の設定ができました。
LEDが4つ点灯しました。
つぎに、入力設定をします。
ピンにタクトスイッチをつなげボタンの入力を見たいと思います。
ボタンを入力するピンは、P25,P24,P23,P22の4つを用います。
10kΩのプルアップ抵抗を用いて各ピンをプルアップしています。このため、ピンにはボタンが押されていないときにはHighが入力され、ボタンが押されているときはLowが入力されます。
ではさきほどの設定につづけてピンの設定をしていきます。
void Pin_initialize(void){ PORTE.PDR.BIT.B1 = 1; // LED0 PORTE.PDR.BIT.B0 = 1; // LED1 PORTD.PDR.BIT.B7 = 1; // LED2 PORTD.PDR.BIT.B6 = 1; // LED3 PORT2.PDR.BIT.B5 = 0; // BUTTON0 PORT2.PDR.BIT.B4 = 0; // BUTTON1 PORT2.PDR.BIT.B3 = 0; // BUTTON2 PORT2.PDR.BIT.B2 = 0; // BUTTON3 }
メイン文ではまず、ボタンが押されたらLEDが光るように処理してみましょう。
#include "iodefine.h" void main(void) { while(1){ if(!PORT2.PIDR.BIT.B2) PORTE.PODR.BIT.B1 = 1; else PORTE.PODR.BIT.B1 = 0; if(!PORT2.PIDR.BIT.B3) PORTE.PODR.BIT.B0 = 1; else PORTE.PODR.BIT.B0 = 0; if(!PORT2.PIDR.BIT.B4) PORTD.PODR.BIT.B7 = 1; else PORTD.PODR.BIT.B7 = 0; if(!PORT2.PIDR.BIT.B5) PORTD.PODR.BIT.B6 = 1; else PORTD.PODR.BIT.B6 = 0; } }
これで入力の設定ができました。
ボタンに合わせてLEDが点灯すれば成功です!
現状ではif文を用いて処理していますが、レジスタ間を直接操作する方法のほうが効率的かと思います。
また、このままではLEDやボタンが扱いにくいので、
#define PORTE.PODR.BIT.B1 LED0 #define PORT2.PIDR.BIT.B2 BUTTON0
などでデファインや変数に入れておくなどすると操作が簡単かと思います。
20160818の記事
Renesas RX631 クロック
ここではクロックの設定をしていきます。
ただ、RX631のクロック設定は非常に難しく、Renesas公式HPにて公開されている「RXショートセミナー」という資料を参考に、クロック設定を行いたいと思います。
といっても、参考資料を少し改変したものなります。
hardware_setup.cにClock_initialize関数を作ります。
#include "iodefine.h" #include "machine.h" void Clock_initialize(void); void HardwareSetup(void) { Clock_initialize(); } void Clock_initialize(void){ int i; SYSTEM.PRCR.WORD = 0xA503; // Release protect SYSTEM.MOSCWTCR.BYTE = 0x0D; // 131072cyc wait stability term // Enable main clock SYSTEM.MOSCCR.BYTE = 0x00; while(SYSTEM.MOSCCR.BYTE == 0x01); SYSTEM.PLLCR.WORD = 0x0F00; // 12Mhz/1*16=192Mhz SYSTEM.PLLCR2.BYTE = 0x00; // Wake up PLL circuit SYSTEM.PLLWTCR.BYTE = 0x0E; // 2097152cyc wait stability term for( i = 0; i <636; i++ ) nop(); // wait 22.1053ms 22.1053ms*143.75khz(LOCO)/5cyc=636 nop()=5cyc // FCLK :192/4=48Mhz // ICLK: 192/2=96Mhz // BCLK: 192/64=3Mhz & disable // PCLKA: 192/2=96Mhz // PCLKB: 192/4=48Mhz // IECLK: 192/4=48Mhz // UCLK: 192/4=48Mhz // Enable PLL circuit SYSTEM.SCKCR.LONG = 0x21C61211; SYSTEM.SCKCR2.WORD = 0x0032; SYSTEM.SCKCR3.WORD = 0x0400; // Stop Unused Clock Source SYSTEM.LOCOCR.BYTE = 0x01; // Stop LOCO SYSTEM.SOSCCR.BYTE = 0x01; // Stop sub clock source while( SYSTEM.SOSCCR.BYTE != 0x01) // wait writing ; RTC.RCR3.BYTE = 0x0C; // Disable clock in while( RTC.RCR3.BYTE != 0x0C ) // wait writing ; SYSTEM.PRCR.WORD = 0xA500; // Protect }
ここで、machine.hはClock_initialize関数内のnop関数を呼ぶために必要なヘッダファイルです。
nop関数は1clockを使った空命令になります。
また、サブクロックは停止させています。
- FCLK :192/4=48Mhz
- ICLK: 192/2=96Mhz
- BCLK: 192/64=3Mhz & disable
- PCLKA: 192/2=96Mhz
- PCLKB: 192/4=48Mhz
- IECLK: 192/4=48Mhz
- UCLK: 192/4=48Mhz
12MHzの水晶発振子をメインクロックに入力し、周辺モジュールを上記クロックでクロック設定しました。
クロック設定はこれで完了です。RX621に比べ非常にややこしなくなっており、現状自分で書くのは不可能だったので、資料を参考にしました。
20160818の記事