2011年3月27日日曜日

グレイコード化による低消費電力もどき

実験プラットフォーム

詳細は別途紹介したいと思いますが、実験プラットフォームとしてLaunchPadのベース基板を使って簡単なソフトウェアの動作テストができる箱を作成しました。

この箱には、LaunchPadのベース基板のSpyByWireとXIN, XOUTを除く全ての汎用ポートに押しボタンスイッチとLEDドライバ(74HC05×3)を接続した回路が搭載されており、LEDとスイッチを取り付けたケースに収納して色々な実験ができるようにしてあります。

スイッチ周りの回路の構成は次の通りですが、ハードウェアによるチャタリング防止機能はつけていません。これは、ソフト的なチャタリング防止の実験や検討を行うためです。

MSP430のピンと押しボタンスイッチの間には100Ωの短絡保護抵抗を直列に挿入し、スイッチの反対側をGNDに落としてあります。

100Ω抵抗と押しボタンスイッチの接点には10kΩのプルアップ抵抗を接続して、押しボタンスイッチ開放時にはHigh状態を維持し、押しボタンスイッチ短絡時にはLowレベルとなるようにしています。

但しMSP430のポートが出力に設定されていてHighが出力されているときには電流を36mAに制限するようになっていますが、元々数mAしか取り出せませんので、保護抵抗の抵抗値は再検討が必要です。

この箱から、LaunchPadで使用できる全ての汎用ポートを外部に引き出して、様々な拡張機能のテストをできるようにするのが中期的な目標ですが、当面はLEDと押しボタンスイッチを使うだけでできる実験をしてみたいと思います。

9分タイマー

汎用ポートに接続された2つの入力スイッチと、6本のLEDを使って0~9分まで1分単位で時間を設定できるタイマーのプログラムを制作しました。

先日、ステートマシンの値の変化をグレイコード化することで消費電力が減るはず、などと書きましたので、状態遷移をグレイコード化したステートマシンを用いるプログラムを組んでみました。

ただ、遷移速度が非常に遅いのであまり効果はなさそうです。むしろ、時間待ちの為のインクリメント・デクリメントの演算をストレートバイナリではなく、グレイコードで行った方が効果的であるように思います。

ちなみに、このプログラムでは待機状態のときに全てのクロックを停止させる仕様になっており、割り込み信号を受けてクロックを起動するようにしています。

以下にサンプルプログラムを元にした制御プログラムを示します。

動作させるためのハードウェア要件としてはMSP430G2シリーズの14ピンタイプの汎用ポート8本が任意に使えることと、XIN, XOUT端子に32.768kHzの水晶振動子を接続していることが条件となります。このため、LaunchPadのベース基板のTXD, RXDのジャンパストラップをオープンにする必要があります。

ソフトウェアの動作としては、以下のようになります。

  • 初期設定の後クロックを全て停止するスリープ状態になります。P1.2またはP1.3に接続されたスイッチがONにされることによる割り込みでスリープ状態を抜け出します。
  • 時刻の設定はP1.2に接続されたスイッチを繰り返し押すことにより、0→1→2→3→4→5→6→7→8→9→0…というように1分単位で行われ、P1.3に接続されたスイッチを押すことによりカウントダウンが始まります。
  • P1.3に接続されたスイッチがスイッチが押されるとカウントダウンを開始します。
  • 設定時刻を経過するとアラーム動作を行い、P1.2またはP1.3に接続されたスイッチが押されることで、アラーム動作を停止してスリープ状態になります。

各動作状態での表示については、以下のようになります。

  • スリープ状態から水晶発振器が安定するまでの期間はP1.1に接続されたLEDを点滅させます。水晶発振器が安定すると、表示に用いるLED6個全てを消灯します。
  • 時刻設定の際には、P1.4, P1.5, P1.6, P1.7 に接続されたLEDを用いて2進数で設定時刻(分)を表示します。
  • カウントダウン中は、P1.0に接続されたLEDを一秒間隔で点滅させながら、1分経過するごとに2進数表示させた設定時刻を1分ずつ減らして行き、最終的には全て消灯させます。
  • カウントダウン終了後は、P1.0に接続されたLEDを短い時間間隔で点滅させます。

以下にソースコードを示します。今回のポイントとなるグレイコード化はソースコードの先頭でステートマシンの状態を定義している箇所で行っています。

ソースコードをごらんいただけば分かるように、INITIALからSTOPまでの状態遷移においては殆どの状態遷移において1bitしか変化しません。但し、ALARMとSTOPの間は2bit変化します。これは、繰り返し処理となるHOLD→STOP→HOLDのステート数がちょうど良い数にならなかったためです。


#include  

// State Machines That Gray Coded Style
#define INITIAL 0x00 // bin 0000_0000
#define HOLD    0x01 // bin 0000_0001
#define MIN_SET 0x03 // bin 0000_0011
#define CNT_DN  0x07 // bin 0000_0111
#define ALARM   0x06 // bin 0000_0110
#define STOP    0x05 // bin 0000_0101

unsigned char state; // External Variable Used to Keep State 
int set_time;

void main (void)
{
  volatile unsigned int i;
  volatile unsigned int sec;
  
  // Initial Settings
  sec = 0x00;
  set_time = 0x00;
  state = INITIAL; // State Machine Initialize
  
  WDTCTL = WDTPW + WDTHOLD;                        // Stop watchdog timer
  BCSCTL3 |= XCAP_3;                               // Set X_CAP 12.5pF
  TACTL = TASSEL_1 + MC_1 + TACLR;                 // ACLK, up mode, Timer CLR
  CCR0 = 0x7FFF;                                   // Set TACCR0 to 16383
  P1DIR |= 0xF3;                                   // Set LED Port Output
  P1OUT &= 0x0C;                                   // LED OFF
  P1IE |= 0x0C;                                    // P1.3 Interrupt Enable
  P1IES |= 0x0C;                                   // P1.3 Fall Edge Interrupt Enable
  P1IFG &= ~0x0C;                                  // P1 Interrupt state Cleared

  // When X'tal OSC is fault, Red LED Blink
  while (BCSCTL3 & LFXT1OF) {
   P1OUT ^= 0x01;                                 // Blink Red LED
 for(i=5000;i>0;i--){;}                         // Set Blink Interval to Visible 
  }
  P1OUT = 0x00;                                    // All LED OFF

  for(;;){
   if(state == INITIAL){
    state = HOLD;             // State set as HOLD
   }
   
   if(state == HOLD){
    _BIS_SR(LPM4_bits + GIE); // All Clocks Stop and Interrupt Enable
   }
   
 if(state == HOLD){
  state = MIN_SET;
 }else if(state == CNT_DN){
  // Toggle Green LED with 1 Seconds Interval
     while(set_time > 0){
      if((TAR & 0xFFFF)==0){
         sec +=1;
          if((sec >= 60) && ((sec % 60) == 0)){
           set_time -= 1;
          }
         P1OUT = 16*set_time + (sec & 0x0001);
      }
     }
     if(set_time == 0){
      state = ALARM;
     }
 }else if(state == ALARM){
     P1OUT = 0x00;

     // Flash Red LED until Push the Button
     while(state==ALARM){
      P1OUT ^= 0x01;
        for(i=5000;i>0;i--){;}
     }

 }else if(state == STOP){
  P1OUT = 0x00;
     sec = 0;
     set_time = 0;
     TACTL |= TACLR;                 // Timer CLR
     state = HOLD;
 }
  }
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void){
  volatile unsigned int i;
  LPM4_EXIT;
  P1IE &=0xF3;
  if(P1IFG & 0x08){
   P1IFG &= ~0x08;                                // P1 Interrupt Flag Cleared
    if((state == MIN_SET) && (set_time>0)){
     state = CNT_DN;
    }else if(state == ALARM){
     state = STOP;
    }
  }else if((P1IFG & 0x04) && (state == ALARM)){
   P1IFG &= ~0x04;                                // P1 Interrupt Flag Cleared
    state = STOP;
  }else if((P1IFG & 0x04) && (state != MIN_SET)){
   P1IFG &= ~0x04;                                // P1 Interrupt Flag Cleared
    state = MIN_SET;
    P1OUT = 0x00;                                  // All LED OFF
  }else if((P1IFG & 0x04) && (state == MIN_SET)){
   P1IFG &= ~0x04;
   if((P1IN & 0x04)==0){
    if(set_time < 0x09){
     set_time += 1;
    }else if(set_time == 0x09){
     set_time = 0;
    }
   }
   P1OUT = (set_time * 16);
  }
  i=0;
  while(i<10000){
   if(P1IN & 0x0C){
    i++;
   }else{
    i=0;
   }
  }
  // When X'tal OSC is fault, LED Blink
  while (BCSCTL3 & LFXT1OF) {
   P1OUT ^= 0x02;                                 // Blink Red LED
   for(i=5000;i>0;i--){;}                         // Set Blink Interval to Visible 
 P1OUT = 0x00;
  }

  P1IE |=0x0C;
}

0 件のコメント:

コメントを投稿

フォロワー