RXマイコンのソフト開発(1)クロック設定とLチカ

今回からRXマイコン(RX631)のソフト開発を行いたいと思います。まずはクロック設定とLチカからはじめたいと思います。主にこの記事は以下のマウス本を参考にしています。また、RX631のユーザーズマニュアル ハードウェア編もかなり参考にしているので、参照することをおススメします。

開発環境

  • パソコン: Windows10 64 bit
    • 統合開発環境: e2studio 2021-01をインストール
    • コンパイラ: Renesas CCRX v3.03.00をインストール
    • 書き込みソフト: Renesas Flash Programmer V3.08.01(無償版) をインストール
  • ターゲットデバイス: R5F5631PDDFL(RX631 48ピン)

プログラム

早速、記述したプログラムが以下です。初期化関数はinit_rx631.cinit_rx631.h、使用するポートの再定義はdefine_wakaba.hに書いています。メインルーチンはmain.cに書いています。GitHubの「1_led」フォルダの中にソースコードがありますので、ダウンロードしてご利用ください。

init_rx631.c:初期化関数のソースファイル

#include "init_rx631.h"
#include "iodefine.h"

void init_rx631(void){
    unsigned short i=0;
    SYSTEM.PRCR.WORD = 0xA503;  // Unprotect

    /*---- SUBCLK and RTC Initialization ----*/
    // SUBCLK Initialization
    SYSTEM.SOSCCR.BIT.SOSTP = 1;    // Sub CLK STOP
    while(SYSTEM.SOSCCR.BIT.SOSTP != 1); // Wait until SOSTP=1
    RTC.RCR3.BIT.RTCEN      = 0;    // Sub CLK STOP
    while(RTC.RCR3.BIT.RTCEN != 0); // Wait until RTCEN=0

    // Main CLK setting
    RTC.RCR4.BIT.RCKSEL     = 1;    // Select main CLK as count source
    SYSTEM.MOSCCR.BIT.MOSTP = 0;    // Main CLK ON
    for(i=0;i<1000;i++);            // Wait about 50ms

    // SYSTEM CLK setting
    SYSTEM.PLLCR.BIT.PLIDIV = 0x0;  // Main CLK divided by 1
    SYSTEM.PLLCR.BIT.STC    = 0x13; // Main CLK multiplied by 20
    SYSTEM.PLLCR2.BIT.PLLEN = 0;    // PLL Enable
    for(i=0;i<1000;i++);            // wait about 50ms
    // FCK: 4div, ICK:  2div, BCK:  stop, SDCLK stop,
    // BCK: 4div, PCKA: 2div, PCKB: 4div
    SYSTEM.SCKCR.LONG       = 0x21C21211;
    SYSTEM.SCKCR3.BIT.CKSEL = 0x4;  // Select PLL as CLK source
    SYSTEM.LOCOCR.BIT.LCSTP = 1;    // LOCO stop

    // RTC stop and reset
    RTC.RCR2.BIT.START      = 0;    // RTC stop
    while(RTC.RCR2.BIT.START != 0); // Wait until START=0
    RTC.RCR2.BIT.RESET      = 1;    // RTC reset
    while(RTC.RCR2.BIT.RESET != 0); // Wait until RESET=0

    // Disable RTC interrupt requests
    RTC.RCR1.BYTE        = 0x00;    // Alarm interrupt disable
    while(RTC.RCR1.BYTE != 0x00);   // wait until RCR1=0x00
    /*---- End of SUBCLK and RTC Initialization ----*/

    // I/O setting
    PORT1.PDR.BIT.B7 = 1;  // LED_Red:   Out
    PORT2.PDR.BIT.B7 = 1;  // LED_Green: Out
    PORTE.PDR.BIT.B3 = 1;  // LED_Blue:  Out

    PORT1.PODR.BIT.B7 = 1; // LED_Red:   HIGH
    PORT2.PODR.BIT.B7 = 1; // LED_Green: HIGH
    PORTE.PODR.BIT.B3 = 1; // LED_Blue:  HIGH

    PORT1.PMR.BIT.B7 = 0;  // LED_Red:   I/O
    PORT2.PMR.BIT.B7 = 0;  // LED_Green: I/O
    PORTE.PMR.BIT.B3 = 0;  // LED_Blue:  I/O

    SYSTEM.PRCR.WORD = 0xA500;  // Reprotect
}

init_rx631.h:初期化関数のヘッダーファイル

#ifndef __INITRX631_HEADER__
#define __INITRX631_HEADER__

void init_rx631(void);

#endif

define_wakaba.h:使用するポートを再定義するヘッダーファイル

#ifndef __DEFINE_WAKABA_H__
#define __DEFINE_WAKABA_H__

#include "iodefine.h"

// reg define
#define LED_RED    PORT1.PODR.BIT.B7
#define LED_GREEN  PORT2.PODR.BIT.B7
#define LED_BLUE   PORTE.PODR.BIT.B3

// others
#define LED_ON   0
#define LED_OFF  1

#endif /* __DEFINE_WAKABA_H__ */

main.c:メインルーチンのソースファイル

#include "define_wakaba.h"

void main(void){
    unsigned short i=0;
    unsigned short j=0;

    init_rx631();
    while(1){
        LED_RED = LED_ON;
        for(i=0;i<10000;i++)
            for(j=0;j<100;j++);
        LED_RED = LED_OFF;
        for(i=0;i<10000;i++)
            for(j=0;j<100;j++);
    }
}

プログラムのフロー

プログラムの流れは図1のようになっています。サブクロックとリアルタイムクロック(RTC)の初期化後に、I/Oポートの設定をして、Lチカさせます。経験的にはサブクロックとRTCは初期化しなくても問題ないのですが、「電源投入時に設定してください」とマニュアルに書いてあるので設定します。

図1:プログラムのフローチャート
図1:プログラムのフローチャート

RTCの初期化の流れは図2のようになっています。RTCは使う予定がないので、RTCの停止や割り込み要求の禁止をします。

図2:リアルタイムクロックの初期化のフローチャート
図2:リアルタイムクロックの初期化のフローチャート

プログラムの解説

プログラムの各処理ごとの説明をしていきます。詳細についてはRX631のユーザーズマニュアル ハードウェア編を参照してほしいと思います。

レジスタライトプロテクトの設定

SYSTEM.PRCR.WORD = 0xA503;  // Unprotect
~略~
SYSTEM.PRCR.WORD = 0xA500;  // Reprotect

RXマイコンにはプログラムが暴走したときに、重要なレジスタの値が書き換わらないようにレジスタライトプロテクション機能があります(参照 マニュアル p.350)。この機能によって、クロック発生回路関連レジスタの値を書き換えることができないため、PRCRのPRC0ビットを”1”としてプロテクトを解除します。また、将来的に消費電力低減機能関連レジスタにも書き込みたいため、PRCRのPRC1ビットを”1”としています。初期化関数の最後には、再度レジスタをプロテクトしてます。

注意点としては、PRCRレジスタを書き換える場合、上位8ビットに”A5h”を書き込むことを忘れないでください。はじめ、私はPRC0ビットだけを書きかえていたので、PRCRが書き換わっていませんでした。マウス本のプログラムを見て、書き換わっていないことに気づきました。

サブクロックの初期化

// SUBCLK Initialization
SYSTEM.SOSCCR.BIT.SOSTP = 1;    // Sub CLK STOP
while(SYSTEM.SOSCCR.BIT.SOSTP != 1); // Wait until SOSTP=1
RTC.RCR3.BIT.RTCEN      = 0;    // Sub CLK STOP
while(RTC.RCR3.BIT.RTCEN != 0); // Wait until RTCEN=0

48ピンではサブクロックを使用できませんが、サブクロック関連のレジスタの中にはリセット後に値が不定となっているものがあるので、ビットを設定する必要があります(参照 マニュアル p.288-p.289)。SOSCCRのSOSTPビットに"1"、RCR3のRTCENビットに"0"を設定することで、サブクロック発振器を停止させています。

プログラムを見ると、while文で意味のないことをやっているように見えます。これは、レジスタの書き込みの完了を待ってから後続の命令を実行するために挿入しています。どうやら、レジスタへの書き込みの場合、レジスタの値はすぐに書き換わらないようです。マニュアルに「書き換えた後は、書き込みの完了を待ってから後続の命令を実行する」という旨の記述があるときは、このようにwhile文で待っています(今回の場合、マニュアル p.283に記載されている)。

リアルタイムクロックの初期化

RTCについても48ピンでは使えませんが、電源投入時にRTC内のレジスタの値は不定となっているため、ビットを設定する必要があります。設定手順の詳細は、マニュアル p.1138の「リアルタイムクロック電源投入時の初期化に関する注意事項」における「(d) RTC を使用しない場合(カウントソースにメインクロックが使用できる場合)」を参照してほしいと思います。ここでは、「メインクロックの設定」と「システムクロックの設定」だけ説明したいと思います。

メインクロックの設定

// Main CLK setting
RTC.RCR4.BIT.RCKSEL     = 1;    // Select main CLK as count source
SYSTEM.MOSCCR.BIT.MOSTP = 0;    // Main CLK ON
for(i=0;i<1000;i++);            // Wait about 50ms

メインクロックの設定手順は以下です。

  1. RCR4のRCKSELビットを"1"にして、カウントソースにメインクロックを選択
  2. MOSCCRのMOSTPビットを"0"にすることで、メインクロックを発振させる
  3. for文でメインクロック発振安定待機時間が経過するまで待つ

経験的にはメインクロックの安定まで待たなくても動作はしますが、念のため待機します。 メインクロック発振安定待機時間  t_{\rm MAINOSCWT} は以下の式で計算できます(参照 マニュアル p.312)。

\displaystyle{
 t_{\rm MAINOSCWT}= t_{\rm MAINOSC} + \frac{n+16384}{f_{MAIN}}
}


私の使用しているセラロック(CSTNE10M0G550000R0)の場合、

  • 発振安定時間: t_{\rm MAINOSC}=15000 [us]
  • 発振周波数: f_{\rm MAIN}=10 [MHz]

であり、MSTSビットは初期値のため、 待機時間は n=262144 サイクルです。

したがって、

\displaystyle{
\begin{align}
 t_{\rm MAINOSCWT}&=15000 [{\rm us} ] + \frac{262144 +16384}{10[{\rm MHz}]}  \\
 &=42853 [{\rm us} ]
\end{align} 
}


になって、余裕をもって 50ms 待っています。

補足1. セラロックの発振安定時間についてはどこにも見当たりませんでしたが、セラロックアプリケーションマニュアルに掲載されていた立ち上がり時間のグラフの最大値が約15ms だったので、発振安定時間: t_{\rm MAINOSC}=15000 [us] としています。

補足2. [for(i=0;i<1000;i++);] だと100msくらい待機しているかもしれません。待機時間が気になる人は繰り返し回数を減らしてもいいと思います。

システムクロックの設定

// SYSTEM CLK setting
SYSTEM.PLLCR.BIT.PLIDIV = 0x0;  // Main CLK divided by 1
SYSTEM.PLLCR.BIT.STC    = 0x13; // Main CLK multiplied by 20
SYSTEM.PLLCR2.BIT.PLLEN = 0;    // PLL Enable
for(i=0;i<1000;i++);            // wait about 50ms
// FCK: 4div, ICK:  2div, BCK:  stop, SDCLK stop,
// BCK: 4div, PCKA: 2div, PCKB: 4div
SYSTEM.SCKCR.LONG       = 0x21C21211;
SYSTEM.SCKCR3.BIT.CKSEL = 0x4;  // Select PLL as CLK source
SYSTEM.LOCOCR.BIT.LCSTP = 1;    // LOCO stop

システムクロックの設定手順は以下です。

  1. PLLCRのPLIDIVビットに"00"を設定して、PLLの入力分周比を1とする
  2. PLLCRのSTCビットに"010011"を設定して、PLLの周波数逓倍率を20倍にする
  3. PLLCR2のPLLENビットに"0"を設定して、PLLを動作
  4. for文でPLLクロック発振安定待機時間が経過するまで待つ
  5. SCKCRを設定して、システムクロックなどを最大動作周波数とする
  6. SCKCR3のCKSELビットに"100"を設定して、クロックソースをPLL出力とする
  7. LOCOCRのLCSTPビットに"1"を設定して、LOCOを停止

PLLクロック発振安定時間の求め方はマニュアル p.315 にあります。計算すると 22126us だったため、メインクロックのときと同じようにfor文で待ちます。

SCKCRの設定では、各クロックの動作周波数が最大動作周波数となるように分周比を決めます(参照 マニュアル p.255)。各クロックの動作周波数と分周比は以下のようになっています。

  • システムクロック (ICLK) = 200 MHz / 2 = 100 MHz
  • 周辺モジュールクロックA (PCLKA) = 200 MHz / 2 = 100 MHz
  • 周辺モジュールクロックB (PCLKB) = 200 MHz / 4 = 50 MHz
  • FlashIFクロック (FCLK) = 200 MHz / 4 = 50 MHz
  • 外部バスクロック (BCLK) = 200 MHz / 2 = 100 MHz

システムクロックと周辺モジュールクロックBだけ設定すればいいのですが、他のクロックも念のため設定しています。 また、SDCLKとBCLKについては使用しないため、端子出力を停止させています。

注意点1. SCKCRレジスタを書き換える場合、下位8ビットに”11h”を書き込むことを忘れないでください。はじめ、私はビットごとに書きかえていたので、SCKCRが書き換わっておらず、そのせいかクロックソースがLOCOのままでした。かふぇルネの質問を見て、書き換わっていないことに気づきました。

注意点2. 設定の順番には気をつけてください。例えば、マニュアル p.261には「PLLCR2.PLLEN ビットが “0”(PLL 動作)のとき、PLLCR レジスタへの書き込みは禁止です。」と書いてあるため、PLLの設定はPLL動作前にする必要があります。

注意点3. マニュアル p.258に「PLL選択時は1分周は設定禁止です」とあることに気をつけてください。私の場合、PLLの周波数逓倍率を20倍にすることでクロックソースを200MHzとして、これを2分周または4分周しています。

I/Oポートの設定

 // I/O setting
PORT1.PDR.BIT.B7 = 1;  // LED_Red:   Out
PORT2.PDR.BIT.B7 = 1;  // LED_Green: Out
PORTE.PDR.BIT.B3 = 1;  // LED_Blue:  Out

PORT1.PODR.BIT.B7 = 1; // LED_Red:   HIGH
PORT2.PODR.BIT.B7 = 1; // LED_Green: HIGH
PORTE.PODR.BIT.B3 = 1; // LED_Blue:  HIGH

PORT1.PMR.BIT.B7 = 0;  // LED_Red:   I/O
PORT2.PMR.BIT.B7 = 0;  // LED_Green: I/O
PORTE.PMR.BIT.B3 = 0;  // LED_Blue:  I/O

LEDを制御する端子 (P17, P27, PE3) の設定手順は以下です。

  1. PDRを設定して、端子を出力に設定する
  2. PODRを設定して、端子をHIGH出力とする
  3. PMRを設定して、端子を汎用入出力ポートとして使用する

LED部分の回路図を図3に示します。

図3:LED部分の回路図
図3:LED部分の回路図

回路図がこのようになっているため、端子をHIGH出力とすることでLEDを消灯させています。

I/OポートのH出力とL出力の繰り返し

while(1){
    LED_RED = LED_ON;
    for(i=0;i<10000;i++)
        for(j=0;j<100;j++);
    LED_RED = LED_OFF;
    for(i=0;i<10000;i++)
        for(j=0;j<100;j++);
}

最後にLEDの点灯と消灯を繰り返すプログラムを書いています。このプログラムでおよそ0.3秒の周期でLEDが点滅します。システムクロックの設定が上手くできていなくてクロックソースにLOCOが用いられている場合、約30秒周期でLEDが点滅しますので、気をつけてください。

ハマったところ

// RTC stop and reset
RTC.RCR2.BIT.START      = 0;    // RTC stop
while(RTC.RCR2.BIT.START != 0); // Wait until START=0
RTC.RCR2.BIT.RESET      = 1;    // RTC reset
while(RTC.RCR2.BIT.RESET != 0); // Wait until RESET=0

はじめ、私はマニュアル p.1964 の「48 ピン製品の使用上の注意事項」をもとにサブクロックとRTCを初期化していましたが、どうしてもRCR2のRESETビットが"0"にならず、while文で動作が止まってしまいました。

原因としては以下の2つがありました。

  • メインクロックが動作していなかった。
  • システムクロックの設定がうまくできていなかった。

マニュアル p.1964 の「48 ピン製品の使用上の注意事項」には、メインクロックを動作させることやシステムクロックを設定することなどは書かれていないので、上記でも述べたように以下を参照するほうがいいと思います。

  • マニュアル p.288-p.289の「サブクロック発振器に関する注意事項」における「(d) サブクロックを使用しない場合
  • マニュアル p.1138の「リアルタイムクロック電源投入時の初期化に関する注意事項」における「(d) RTC を使用しない場合(カウントソースにメインクロックが使用できる場合)

また、マニュアル p.256の「クロック発生回路のブロック図」を見て、RTCで使用するクロックはシステムクロックとは関係ないと思っていましたが、マニュアル p.1140「LOCO クロックの周波数はメインクロックの周波数より低いため、システムクロックを変更する必要があります」やマニュアル p.1134「周辺モジュールクロック(PCLK)周波数はカウントソースの周波数以上に設定する」と記述があるので、ICLKやPCLKも関係するみたいです。

とにかく、メインクロックの設定とシステムクロックの設定をすることで、リセットが動作するようになりました。私と同じようにリセットが完了しない人は、メインクロックの設定とシステムクロックの設定がきちんとできているか確認するべきだと思います。

プログラムの動作

RXマイコンにプログラムを書き込む方法の手順でプログラムを書き込みます。 書き込みが完了すれば、シングルチップモードで動作させることで図4のようにLチカが動作します。

図4:Lチカの様子
図4:Lチカの様子

おわりに

結構長くなってしまいましたが、参考にしていただければ幸いです。次回はスイッチ入力を受けつけるプログラムを書こうと思います。

お問い合わせフォーム プライバシーポリシー

© 2021 Setoti All rights reserved.