MSP430F42x实现交流(AC)电压计[日文、附源码]
日本玩家博客:http://hamayan.blog.so-net.ne.jp/2007-02-10
有懂日文的网友或有朋友懂日文的,麻烦翻译一下供国内网友参考吧。先谢谢!
MSP430-CQ ベースボードのアプリケーション AC電圧計
[IMGA=0,absMiddle]http://hamayan.blog.so-net.ne.jp/blog/_images/blog/_800/hamayan/6435676.jpg[/IMGA]
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;
}
}
[IMGA=0,absMiddle]http://hamayan.blog.so-net.ne.jp/blog/_images/blog/_800/hamayan/6303830.jpg[/IMGA]