SSブログ

MSP430-CQ ベースボードのアプリケーション AC電圧計 [MSP430]

birdさんがD/Aを使ってオルゴールを作っているので、こちらはA/Dを使ってみます。まああちらほど複雑な事はしていませんが。

A0入力に、150Vrms入ると0.397Vrms出力するトランスを挟んで家庭用のAC電源を入れています。

トラ技1月号の記事でAC入力が何故このA/Dで取れるか解説していますが、実際は駄目ですね。
結局直流レベルが不定なので、一時的に(おそらく入力端子等の寄生容量がチャージされて)直流電圧が安定しても、その後入力変動が有ると別の直流電圧に落ち着いてしまい、入力が変動する度に直流分が変わってしまいます。
なので適当な、しかし入力範囲を超えない所へバイアスするのが宜しい様です。例えばVCCとAGNDの中点とか、面倒で無ければVREFをバッファ出力にして、これで直流バイアスを入れるとかです。

後は商用周波数の適当な倍数のサンプリング結果(今回は16倍オーバーサンプリング)の二乗平均を取れば実効値を算出できます。
特にfloatを使わずlongでも、このLCDの出せる精度なら十分実用的な計算を出来るとは思いますが、取り敢えず浮動小数点で演算してみました。
まあ実際の所自宅でやったので計測器がテスター程度しかなく、正確な調整はしていませんが、大体それっぽく表示しています。

計算を簡素化すれば3相交流も計測出来るでしょう。
かいつまんで、使用したプログラムのリスト

※iodefine.hは、ビットフィールドを定義したヘッダーファイルです。まだ全部のレジスタについて定義した訳ではありませんが、試してみるならここです。

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "msp430x42x0.h"
#include "iodefine.h"

#define  _MCLK_         (6160384UL)
#define  _SAMPLING_FREQ_  (50 * 16)  //関東なので50Hz

/*******************/
/* 大域変数の皆さん         */
/******************/
float rms;
int disp_value;

/*******************/
/* main                       */
/******************/
void main(void)
{
  unsigned int i;

  WDT_CTL.WORD = WDTPW + WDTHOLD;  // WDTの停止
  SCFQCTL = 94 - 1;
  FLL_CTL0 = DCOPLUS + XCAP18PF;
  SCFI0 = FLLD_2 + FN_2;

  P1_D.BIT.B0 = 1;  //p1_0を出力に設定
  P1_O.BIT.B0 = 0;  //p1_0を出力に設定
  P6_SEL.BYTE = 0x03;  // P6.0=A0+,P6.1=A0-
  P6_O.BIT.B4 = 0;  //p6_4に0を出力する
  P6_D.BIT.B4 = 1;  //p6_4に0を出力する

  SD16_CTL.BIT.SSEL = 1;  //SMCLK
  SD16_CTL.BIT.DIV = 1;  // 1/2
  SD16_CTL.BIT.XDIV = 1;  // 1/3
  SD16_CTL.BIT.VMIDON = SD16_CTL.BIT.REFON = 1;  //VREFとVMIDバッファをON
  //1ms程度の待ちを入れる
  for( i = 0; i < (_MCLK_ / 1000 / 5); i++ ) ;
  SD16_CTL.BIT.VMIDON = 0;  //VMIDバッファをOFF

  SD16_CCTL0.BIT.BUF = 3;  //バッファ有り、高速
  SD16_CCTL0.BIT.OSR = 3;  //32倍オーバーサンプリング
  SD16_CCTL0.BIT.DF = 1;  //2の補数形式

  SD16_INCTL0.BIT.INTDLY = 0;  //最初の3回の変換は無視をする
//  SD16_INCTL0.BIT.GAIN = 0;  // x1

  init_LCD();  // LCDの初期化
  cls();  // LCD表示のクリア

  //BASIC TIMERの設定
  BT_CTL.BIT.SSEL = 0;  // クロック源はACLK/256
  BT_CTL.BIT.DIV = 1;  // クロック源はACLK/256
  BT_CTL.BIT.IP = 4;  //ベーシックタイマーは約4Hz
  BT_CTL.BIT.FRFQ2 = 0;  //LCDクロックはfACLK/32
  BT_CTL.BIT.HOLD = 0;  //カウント動作継続

  //Timer A3の設定
  TACCR0 = (_MCLK_ / 1 / _SAMPLING_FREQ_) - 1 + 1;
  TA_CTL.BIT.SSEL = 2;  //SMCLKを選択
  TA_CTL.BIT.MC = 1;  //UPモード、終わりはCCR0

  TA_CCTL0.BIT._CCIE = 1;  //割り込み許可

  SD16_CCTL0.BIT.IE = 1;  //ADの割り込み許可
  IE_2.BIT.BT = 1;  // Basic Timer割込みを許可
  disp_3dp(1);  // 3DP表示制御. dat=0;表示オフ, dat=1;表示オン

  _BIS_SR(LPM0_bits + GIE);  // LPM0に入る, 周辺モジュール割込み許可
}

/*************************/
/* TIMER A3割込みサービスルーチン   */
/* ()Hz周期                                */
/*************************/
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
  SD16_INCTL0.BIT.INCH = 0;
  SD16_CCTL0.BIT.SC = 1;  //変換開始
}

/***************************/
/* BASICTIMER 割込みサービスルーチン  */
/* 0.25s周期                                */
/***************************/
#pragma vector = BASICTIMER_VECTOR
__interrupt void BASICTIMER (void)
{
  short temp = disp_value;
  
  _BIS_SR(GIE);
  disp( temp );
}

/**************************/
/* A/D変換終了 割込みサービスルーチン */
/**************************/
#pragma vector = SD16_VECTOR
__interrupt void SD16_A_Int (void)
{
#define  ROOT2  (1.4142135623730950488016887242097)
  short data;
  float temp;
  static int count;
  
  data = SD16MEM0;  //変換データの取り込み
  SD16_CCTL0.BIT.SC = 0;  //変換停止

  data -= 35;  //直流offsetの除去
//  avg += data;
  temp = (data * (150 * ROOT2)) / (32768 * 0.562 / 0.6);
  rms += temp * temp;
  
  if( ++count >= 16 )
  {
    disp_value = sqrt( rms * 10000 / 16 );  /*平方根が抜けておりました。*/
//    disp_value = avg / 16;
    count = rms = 0;
  }
}

nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。