Technical Information/GR-CITRUS

Edit  

GR-CITRUS の省電力機能

2019/10/19

GR-CITRUS は単純なループ動作で42mA程度の電流を消費しますので、バッテリーでの長時間動作には不向きです。標準のライブラリにはありませんが、マイコン自体は省電力機能を備えていますので評価してみます。

WS000001.JPG

 
Edit  

方針

  • Arduino言語を用いる
  • 開発環境はIDE for GR を使用
  • 定期的に省電力状態から復帰し、所定の動作を終えた直後に再び省電力状態に移行する
 
Edit  

動作説明

  • RX631で大幅に省電力化できるのはソフトウェアスタンバイモードかディープスタンバイモードですが、後者はより省電力であるもののリセット状態で復帰します。また、GR-CITRUS自体の暗電流が700uA程度(VBUS分圧抵抗350uA+LDO自己消費電流350uA)あり、後者の優位性があまりありません。これらの理由により、今回は使い勝手の良いソフトウェアスタンバイモードを用います。(実測では、CPU自体の消費電流は前者が180uA、後者が80uAといったところです)
  • ソフトウェアスタンバイモードで動作できるタイマはRTCのみです。
  • RTCでソフトウェアスタンバイモードの復帰条件にできる割込は、周期割込、アラーム割込のいずれかです。以下の3例を作りました。(最後のは検証しきれてませんが多分問題無いです)
    1. 周期割込を用い、GR-CITRUSのオンボードLEDをフラッシュ (1/256, 1/128, 1/64, 1/32, 1/16, 1/8, 1/4, 1/2, 1, 2 秒のいずれかを指定)
    2. アラーム割込を用い、GR-CITRUSのオンボードLEDをフラッシュ(秒指定で、1秒以上24時間以下)
    3. アラーム割込を用い、GR-CITRUSのオンボードLEDをフラッシュ(秒指定で、1秒以上~上限なし)
 
Edit  

ソフトウェア

  1. 周期割込使用( 1/2^8s(=1/256s)~1/2^-1s(=2s) )
    // RTCの周期割込を利用したソフトウェアスタンバイモード動作
    // 1/2秒周期のRTC周期割込をソフトウェアスタンバイモードの
    // 復帰トリガとし、1/2秒毎にLEDをフラッシュさせる。
    // ソフトウェアスタンバイ中は消費電流が 1mAを切る省電力状態となる。
    
    #include <RTC.h>
    RTC_TIMETYPE t;
    
    int led = PIN_LED0;  // 4
    
    void ensleep(void) {  // RX631をソフトウェアスタンバイモードにする
      SYSTEM.PRCR.WORD = 0xA503u;  // Protection off
      SYSTEM.SBYCR.BIT.SSBY = 1;   // WAIT命令後にソフトウェアスタンバイモードに遷移
      while(!SYSTEM.SBYCR.BIT.SSBY) ;
      __asm("wait"); 
      SYSTEM.PRCR.WORD = 0xA500u;  // Protection on
    }
    
    void setup() {
      pinMode(led, OUTPUT);
      // リセット後の動作:200ms周期で3回LEDを点滅
      for (int i=0;i<3;i++){
        digitalWrite(led,HIGH);
        delay(100);
        digitalWrite(led,LOW);
        delay(100);
      }
      rtc_init();  //リアルタイムクロックを有効化
      //ダミーの時刻を設定する
      t.year    = 2000;
      t.mon     = 1;
      t.day     = 1;
      t.weekday = RTC_WEEK_SATURDAY;
      t.hour    = 0;
      t.min     = 0;
      t.second  = 0;
      rtc_set_time(&t);  // ここで初めてRTCが動作し始める
      
      // 周期割り込み1/2秒 および周期割り込み許可を有効に
      RTC0.RCR1.BYTE = 0xd4;
      while (RTC0.RCR1.BYTE != 0xd4) ;  // レジスタ設定が反映されるまで待機
      IPR(RTC, PRD) = 3;  // RTCの周期割込の割込優先度を3に設定
      IEN(RTC, PRD) = 1;  // RTCの周期割込を許可
      IR(RTC, PRD)  = 0;  // RTCの周期割込の割込フラグをクリア
    }
    
    void loop() {
      digitalWrite(led,HIGH);
      delay(10);
      digitalWrite(led,LOW);
      ensleep();  // 次のRTC周期割り込みがかかるまでソフトウェアスタンバイモードへ移行
    }
  2. アラーム割込使用(1s~24h)
    // RTCのアラーム割込を利用したソフトウェアスタンバイモード動作
    // 1秒周期のRTCアラーム割込をソフトウェアスタンバイモードの
    // 復帰トリガとし、1秒毎にLEDをフラッシュさせる。
    // ソフトウェアスタンバイ中は消費電流が 1mAを切る省電力状態となる。
    // RTCを動作させたままアラーム時間を都度変更していくため、周期は正確。
    // 設定できる間欠動作の周期は24時間以内で、秒数で設定する。
    // 24時間を超える値に設定された場合は、24時間で割った余りに設定する。
    //
    
    #include <RTC.h>
    #include <time.h>
    RTC_TIMETYPE t;
    
    int led = PIN_LED0;
    
    static inline uint8_t HEX2BCD(int s16HEX)
    {
        return ((s16HEX / 10) << 4) | (s16HEX % 10);
    }
    /**
     * アラーム時間を(時、分、秒)で設定します。
     *
     * @param[in] hour      時を指定します。
     * @param[in] min       分を指定します。
     * @param[in] sec       秒を指定します。
     *
     * @retval 0:アラームの設定に失敗しました。
     * @retval 1:アラームの設定に成功しました。
     *
     * @attention 時刻の値はBCDではありません。
     ***************************************************************************/
    int rtc_set_alarm_time_hms(int hour, int min, int sec)
    {
        /* Configure the alarm as follows -
            Alarm time - 12:00:00
            Enable the hour, minutes and seconds alarm      */
        RTC0.RSECAR.BYTE = HEX2BCD(sec);
        RTC0.RMINAR.BYTE = HEX2BCD(min);
        RTC0.RHRAR.BYTE  = HEX2BCD(hour);
    
        RTC0.RSECAR.BIT.ENB = 1;
        RTC0.RMINAR.BIT.ENB = 1;
        RTC0.RHRAR.BIT.ENB  = 1;
        RTC0.RDAYAR.BIT.ENB = 0;
        RTC0.RMONAR.BIT.ENB = 0;
        RTC0.RYRAREN.BIT.ENB = 0;
        RTC0.RWKAR.BIT.ENB = 0;
    
        /* Enable alarm and interrupts*/
        RTC0.RCR1.BIT.AIE = 1;
        while(!RTC0.RCR1.BIT.AIE);
    
        /* Enable RTC Alarm interrupts */
        IPR(RTC, ALM) = 3u;
        IEN(RTC, ALM) = 1u;
        IR(RTC, ALM)  = 0u;
    
        return 1;
    }
    
    void ensleep(void) {  // RX631をソフトウェアスタンバイモードにする
      SYSTEM.PRCR.WORD = 0xA503u;  // Protection off
      SYSTEM.SBYCR.BIT.SSBY = 1;   // WAIT命令後にソフトウェアスタンバイモードに遷移
      while(!SYSTEM.SBYCR.BIT.SSBY) ;
      __asm("wait"); 
      SYSTEM.PRCR.WORD = 0xA500u;  // Protection on
    }
    
    void set_wakeup_period(int sec){  // 間欠動作の周期を設定する 24時間以内の秒数で設定
      RTC_TIMETYPE t_now;
      int sumsec, tmp, next_h, next_m, next_s;
    
      if(sec >86400){        // 間欠動作の周期が24時間を超える場合は、24時間で割った余りに設定
        sec = sec % 86400;
      }
      rtc_get_time(&t_now);    // RTCから現在時刻を取得
      sumsec = t_now.hour*3600 + t_now.min*60 + t_now.second;  //秒数に変換
      tmp = sumsec + sec;      //次のアラーム時刻の「秒」表現
      if( tmp >= 86400 ){      //86400=3600*24  24時間以上であるなら
        tmp = tmp - 86400;     //86400=3600*24  24時間を減ずる
      }
      next_h = tmp / 3600;
      next_m = (tmp % 3600) / 60;
      next_s = (tmp % 3600) % 60;
      rtc_set_alarm_time_hms(next_h,next_m,next_s);  // 次のアラーム時刻を設定
    }
    
    void setup() {
      pinMode(led, OUTPUT);
      // リセット後の動作:200ms周期で3回LEDを点滅
      for (int i=0;i<3;i++){
        digitalWrite(led,HIGH);
        delay(100);
        digitalWrite(led,LOW);
        delay(100);
      }
      rtc_init();  //リアルタイムクロックを有効化
      t.year = 2000;
      t.mon = 1;
      t.day = 1;
      t.weekday = RTC_WEEK_SATURDAY;
      t.hour    = 23;  //0
      t.min     = 59;  //0
      t.second  = 55;  //0
      rtc_set_time(&t);  // ここで初めてRTCが動作し始める
    }
    
    void loop() {
      digitalWrite(led,HIGH);
      delay(10);
      digitalWrite(led,LOW);
      set_wakeup_period(1); // 1秒後にアラームをセット
      ensleep();  // 次のアラーム割り込みがかかるまでソフトウェアスタンバイモードへ移行
    }
  3. アラーム割込使用(1s~上限なし)
    // RTCのアラーム割込を利用したソフトウェアスタンバイモード動作
    // 1秒周期のRTCアラーム割込をソフトウェアスタンバイモードの
    // 復帰トリガとし、1秒毎にLEDをフラッシュさせる。
    // ソフトウェアスタンバイ中は消費電流が 1mAを切る省電力状態となる。
    // RTCを動作させたままアラーム時間を都度変更していくため、周期は正確。
    //
    // IDE for GRのデフォルトの開発環境ではRTCのtimezoneはGMTに設定されて
    // いるので、 localtime()とgmtime()による取得時間は一致する。したがって、
    // mktime()関数によるlocal時間の1970年1月1日0:0:0からの経過秒数(UNIX時間)
    // は双方で一致する。
    // また、RTC関連のC言語標準ライブラリの設定はうるう秒に対応していない為、
    // マイコンのRTCから取得した日時データをC言語標準ライブラリにてUNIX時間に
    // 変換し加工したあとでC言語標準ライブラリにてマイコン用のRTCの日時に
    // 変換し直しても特に問題は生じない。
    // 
    
    #include <RTC.h>
    #include <time.h>
    RTC_TIMETYPE t;
    
    int led = PIN_LED0;
    
    static inline uint8_t HEX2BCD(int s16HEX)
    {
        return ((s16HEX / 10) << 4) | (s16HEX % 10);
    }
    /**
     * アラーム時間を設定します。
     *
     * @param[in] year      年を設定します。
     * @param[in] mon       月を設定します。
     * @param[in] day       日を指定します。
     * @param[in] hour      時を指定します。
     * @param[in] min       分を指定します。
     * @param[in] sec       秒を指定します。
     * @param[in] week_flag 曜日を指定します。複数の曜日を指定する場合は論理和で接続します。
     *
     * @retval 0:アラームの設定に失敗しました。
     * @retval 1:アラームの設定に成功しました。
     *
     * @attention 時刻の値はBCDではありません。
     ***************************************************************************/
    int rtc_set_alarm_time_all(int year, int mon, int day, int hour, int min, int sec, int week_flag)
    {
        /* Configure the alarm as follows -
            Alarm time - 12:00:00
            Enable the hour, minutes and seconds alarm      */
        RTC0.RSECAR.BYTE = HEX2BCD(sec);
        RTC0.RMINAR.BYTE = HEX2BCD(min);
        RTC0.RHRAR.BYTE  = HEX2BCD(hour);
        RTC0.RDAYAR.BYTE = HEX2BCD(day);
        RTC0.RMONAR.BYTE = HEX2BCD(mon);
        RTC0.RYRAR.WORD  = HEX2BCD(year % 100);
        if(week_flag <= 0x06){
            RTC0.RWKAR.BYTE  = week_flag;
        }
    
        RTC0.RSECAR.BIT.ENB = 1;
        RTC0.RMINAR.BIT.ENB = 1;
        RTC0.RHRAR.BIT.ENB  = 1;
        RTC0.RDAYAR.BIT.ENB = 1;
        RTC0.RMONAR.BIT.ENB = 1;
        RTC0.RYRAREN.BIT.ENB = 1;
        if(week_flag <= 0x06){
            RTC0.RWKAR.BIT.ENB = 1;
        } else {
            RTC0.RWKAR.BIT.ENB = 0;
        }
    
        /* Enable alarm and interrupts*/
        RTC0.RCR1.BIT.AIE = 1;
        while(!RTC0.RCR1.BIT.AIE);
    
        /* Enable RTC Alarm interrupts */
        IPR(RTC, ALM) = 3u;
        IEN(RTC, ALM) = 1u;
        IR(RTC, ALM)  = 0u;
    
        return 1;
    }
    
    void ensleep(void) {  // RX631をソフトウェアスタンバイモードにする
      SYSTEM.PRCR.WORD = 0xA503u;  // Protection off
      SYSTEM.SBYCR.BIT.SSBY = 1;   // WAIT命令後にソフトウェアスタンバイモードに遷移
      while(!SYSTEM.SBYCR.BIT.SSBY) ;
      __asm("wait"); 
      SYSTEM.PRCR.WORD = 0xA500u;  // Protection on
    }
    
    void set_wakeup_period(int sec){
      RTC_TIMETYPE t_now;
      struct tm tn;
      time_t timer;
      
      rtc_get_time(&t_now);  // RTCから現在時刻を取得
      tn.tm_year = t_now.year-1900;
      tn.tm_mon  = t_now.mon-1;
      tn.tm_mday = t_now.day;
      tn.tm_wday = t_now.weekday;
      tn.tm_hour = t_now.hour;
      tn.tm_min  = t_now.min;
      tn.tm_sec  = t_now.second;
      
      timer = mktime(&tn);      // 1970年からの秒数に変換
      timer += sec;            // 次のアラーム時刻(秒表現)の算出
    
      tn = *localtime(&timer);  // 1970年からの秒データを日付に変換
      rtc_set_alarm_time_all(tn.tm_year+1900, tn.tm_mon+1, tn.tm_mday, tn.tm_hour, tn.tm_min, tn.tm_sec, 0xff);
    }
    
    void setup() {
      pinMode(led, OUTPUT);
      // リセット後の動作:200ms周期で3回LEDを点滅
      for (int i=0;i<3;i++){
        digitalWrite(led,HIGH);
        delay(100);
        digitalWrite(led,LOW);
        delay(100);
      }
    
      rtc_init();  //リアルタイムクロックを有効化
      t.year = 2000;
      t.mon = 1;
      t.day = 1;
      t.weekday = RTC_WEEK_SATURDAY;
      t.hour    = 0;
      t.min     = 0;
      t.second  = 0;
      rtc_set_time(&t);  // ここで初めてRTCが動作し始める
      
    }
    
    void loop() {
      digitalWrite(led,HIGH);
      delay(10);
      digitalWrite(led,LOW);
      set_wakeup_period(1); // 1秒後にアラームをセット
      ensleep();  // 次のアラーム割り込みがかかるまでソフトウェアスタンバイモードへ移行
    }
 
Edit  

性能評価

  • ソフトウェアスタンバイ時は0.9mA程度の消費電流となります。Arduino環境のnopループでは42mA程度消費していますので、消費電力は約1/45に低減できることになります。例えばバッテリー駆動で10分に1回センサ処理するような省エネ機能の場合は、45倍程度の動作時間になります。





トップ   リロード   一覧 単語検索