RXマイコンやSuperHなどのルネサス製マイコンのC/C++コンパイラは,ターゲットデバイスに応じたiodefine.hというヘッダを自動的に生成します.このファイルの使い方についてここでは説明します.

  1. レジスタの設定
    ルネサス製マイコンは,レジスタを特定のメモリ上にマッピングしており,そのアドレスにある値を読み書きすることでレジスタを操作することができます.従って,各レジスタがマッピングしているアドレスをすべて知っていれば操作できますが,マイコンの種類により異なり,かつ多くのレジスタがあるため,知っておくのは大変です.そこでiodefine.hが活躍するのです.
    iodifine.hにはすべてのレジスタを構造体と共用体を利用して定義しています. ここではポートCの操作に関連するレジスタを例にして説明します.RX62Nのプロジェクトを作成すると生成されるiodefine.hのxx行からyy行にポート0に関する構造体が定義されています.下にそれを抜粋して掲載します.
    struct st_portc {
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } DDR;
      char wk0[31];
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } DR;
      char wk1[31];
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } PORT;
      char wk2[31];
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } ICR;
      char wk3[31];
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } ODR;
      char wk4[63];
      union {
        unsigned char BYTE;
        struct {
          unsigned char B7 : 1;
          unsigned char B6 : 1;
          unsigned char B5 : 1;
          unsigned char B4 : 1;
          unsigned char B3 : 1;
          unsigned char B2 : 1;
          unsigned char B1 : 1;
          unsigned char B0 : 1;
        } BIT;
      } PCR;
    };

     
    構造体st_portcには6個のメンバがあり,さらにそれらのメンバは共用体となっています.共用体では,同じメモリ上に異なるビット長のメンバを存在させることができます.このため,例えば1ビットずつアクセスする方法と,複数のレジスタをまとめてアクセスする方法が実現できます.なお,メンバ名の後ろにある「: 数字」はビットフィールドといい,書かれた数字の長さ(単位はビット)のメンバとして扱われます.

    プレゼンテーション
    上にポートCに関連するレジスタの模式図を示します.DDRなどのレジスタはすべてst_portc構造体のメンバとなっています.また,wk0~wk4は各レジスタのアドレスを調整するための配列です.さて,DDRなどのレジスタはBYTEとBITのメンバを持つ共用体となっています.このため,BYTEとBITは共に同じアドレス上に存在し,片方のメンバにより値を変更するともう片方のメンバの値も変更されます.さらに,BITは構造体となっており,B0~B7のメンバを持っています.

    iodefine.hではさらに,st_portc構造体はレジスタが存在するアドレスにPORTCとしてマッピングしてあります.マッピングするための#defineをiodefine.h~抜粋して下に示します.PORTCのDDRはアドレス0x8C00Cから始まる場所にマッピングされていることが分かります.

    #define PORTC   (*(volatile struct st_portc   __evenaccess *)0x8C00C)


    次に例を挙げて実際の使い方を学びましょう.例として,ポートCビット0~3を入力端子にするプログラムを示します.このために用いるレジスタはDDR(データディレクションレジスタ)であり,入力にしたい端子に対応するビットを0にすると入力端子となります.
    下のプログラムはBIT構造体を使った例です.各ビットごとに0を代入することで,対応する端子を入力端子とすることができます.

    PORTC.DDR.BIT.B0 = 0;
    PORTC.DDR.BIT.B1 = 0;
    PORTC.DDR.BIT.B2 = 0;
    PORTC.DDR.BIT.B3 = 0;


    下のプログラムはBYTEを使った例です.ビット0~3のみを0にし,ビット4~7を変化させないようにするため,もともとのBYTEと0xF0の論理積を計算し,BYTEに格納します.このように,BYTEを利用すると1行で同じことが実現できますが,やや複雑なビット操作をしなければなりません.

    PORTC.DDR.BYTE &= 0xF0;

     

  2. 割込みの設定
    割込みを行うには,割込みの可否と優先順位を決める必要があります.これらを設定するとき便利なのが,iodefine.hにマクロとして定義されたIENとIPRです.たとえば,コンペアマッチタイマ0について割り込みを許可するには次のように書きます.
    IEN(CMT0,CMI0) = 1;

    CMT0やCMI0は同じくiodefine.hに定義されており,結果としてコンペアマッチタイマ0の割り込み可否を定義してある以下のレジスタが1となります.

    ICU.IER[0x03].BIT.IEN4 = 1;

     同じように,割り込みの優先順位を設定するにはIPRを使います.例えばシリアルコミュニケーション0のエラー時に割り込みをさせたい場合,以下のように書きます.

    IPR(SCI0,ERI0) = 3;

     以上のように,いちいち各レジスタの要素番号をデータシートで確認せずとも,割り込みの可否および優先順位を決められます.

  3. 周辺モジュールの動作開始と停止
    各周辺モジュールには無駄な電力を消費しないようにするため,スタンバイ機能があり,多くの場合,初期状態でスタンバイとなっています.従って,使いたい周辺モジュールを最初にスタンバイ解除をしないといけません.この時に使われるマクロがMSTPです.例えばタイマ2のスタンバイを解除したい場合には以下のように書きます.
    MSTP(TMR2) = 0;

    このようにマクロの引数としてスタンバイを解除したい周辺モジュール名を入れればよいです.なお,右辺の0を1にすればスタンバイ状態となります.