AD変換とシリアル送信ができるようになったので、試しに電源トランスで降圧した交流電圧の測定をやってみた。
AD変換入力の最大値をAVRの電源電圧5Vを超えないように、2.5Vがセンターになるように、プルアップして、AVR の PC1 に接続。さらにセンターの2.5Vの電位はPC0 に接続し、PC1 との差をとって測定値が得られる。回路は arms22 さんのものと同じです。ただしトランスの電圧は異なるので、分圧抵抗値は 3.3KΩ:37KΩにしています。最初は 3.3KΩ:27KΩでしたが、測定値がオーバーしそうだったので、10KΩを足しました。
arms22 さんのスケッチでは、1サイクルのサンプル数が25個です。したがって、50Hzの場合は周期 0.02[sec]を25で割って 0.0008[sec]=0.8[mSec]間隔でサンプルしていかねばなりません。当初、AD変換と「同時に」データ送信もしなければならない、と考えていたので、サンプルした4個のデータ(単相3線なので、VとAを2個ずつ)を同時に送信できるシリアル通信のスピードを計算すると、250Kbbpsが必要ということになりました。が、後から考えると、単にストアしたデータをデータ収集が終わってから送ればよいことに気がつきました。
そもそも、実験では、これだけのデータを送信しますが、
実際、電力計としての機能を実現するにはこれほどのデータをホストに送る前にAVRで計算した値だけ送ればよいのですが、電圧と電流の波形のずれを実際にみてみたかったため、このようになりました。
値をエクセルでグラフに表示して、正弦波が得られるとちょっとうれしいです。
商用電源は1秒間に50回も繰り返して点滅しているのに、マイコンはその波をさらに多くの点に分割して、データを得ているのを見ると、この程度のマイコンで、最低速度のAD変換でも、こんなにコンピュータは早いのだ、と思えます。
AVR 側プログラム
//AD_Conv3 はAD変換しながらデータ送信するようになっているが、
//その方法では、0.8mSec 以内にループが回らないようなので、
//データは一旦、配列にストアして、AD変換終了後に送信する
//方式に変更
// [Current Configration Options]に mega88 を設定すること
// コンパイラオプション:最適化なし -O0
// CKDIV8 0(チェックなし)
// CKOUT 0
// CK clock 8MHz
//シリアル変換IC ADM3202AN
//動作OK
// _delay_ms(1); // マルチプレクサが安定するまで待つ を当初入れいたら、AD変換速度を 1/2 に設定しても
//まったく追いつかなかった。これを外したら劇的に早くなった。AD変換速度は 1/128 でOK。
//AC からの入力で AD変換結果が ファイルにデータとして記録できる。
//CSVファイルフォーマット: Col1, Col2, Col3
//Col1: PC1 (測定値)
#define F_CPU 8000000UL //8MHz
#include
#include
#include
#define FOSC 8000000 // 8MHz
#define BAUD 19200
#define MYUBRR (FOSC/16/BAUD)-1 // UART分周率
#define NumOfSample 100
void wait( unsigned char );
//分周率設定 データシート表20-9
//CPU 8MHz, U2X=0, Baud=240K -> UBRR=1 (が、PC 側に240Kのスピードは無いので 19200bpsで断念)
void sio_init(unsigned int baud){
UBRR0H = (unsigned char)(baud>>8); // ボーレート上位
UBRR0L = (unsigned char)baud; // ボーレート下位
// UCSR0A = (UCSR0A | 0b00000010); //U2X0 設定 (倍速=1) (デフォルト 0)
UCSR0B = (1< UCSR0C = (1< } void sio_putint4(int16_t num1,int16_t num2,int16_t num3,int16_t num4) //4個のint値を一度にハンドル { while ( !(UCSR0A & (1< UDR0 = (int8_t)(num1>>8); //上位バイトを先に送信したほうが受信側処理がしやすい while ( !(UCSR0A & (1< UDR0 = (int8_t)(0x00ff&num1); while ( !(UCSR0A & (1< UDR0 = (int8_t)(num2>>8); //上位バイトを先に送信したほうが受信側処理がしやすい while ( !(UCSR0A & (1< UDR0 = (int8_t)(0x00ff&num2); while ( !(UCSR0A & (1< UDR0 = (int8_t)(num3>>8); //上位バイトを先に送信したほうが受信側処理がしやすい while ( !(UCSR0A & (1< UDR0 = (int8_t)(0x00ff&num3); while ( !(UCSR0A & (1< UDR0 = (int8_t)(num4>>8); //上位バイトを先に送信したほうが受信側処理がしやすい while ( !(UCSR0A & (1< UDR0 = (int8_t)(0x00ff&num4); } int main( void ) { int ref,volt; int volt_array[NumOfSample]; sio_init(MYUBRR); // USART設定 for(int16_t i=2;i<30;i++) { //最初の数秒はPCとの同期のため、先頭を 1 で送信しておく sio_putint4(1,i,i,i); _delay_ms(100); } ADCSRA = 0b10000111; // ADイネーブル // | +++-------1/16クロック,1/16=62.5kHz。1/128で最遅7.8125KHz。これでも電話音声程度なら十分。 // +--------------ADイネーブル for(int8_t i=0;i ADMUX = 0b00000000; // 基準,入力選択 // || ++++-------PC0選択 // |+------------右そろえ // +-------------基準電圧の内部接続切り,Arefピンの電圧を使う // _delay_ms(1); // マルチプレクサが安定するまで待つ ADCSRA = 0b11000111; // ADイネーブル // | +++-------1/16クロック,1/16=62.5kHz。1/128で最遅7.8125KHz。これでも電話音声程度なら十分。 // +--------------ADイネーブル while( ADCSRA & 0b01000000 ) // 変換終了待ち ; ref = ADC; ADMUX = 0b00000001; // 基準,入力選択 // || ++++-------PC1選択 // |+------------右そろえ // +-------------基準電圧の内部接続切り,Arefピンの電圧を使う // _delay_ms(1); // マルチプレクサが安定するまで待つ ADCSRA = 0b11000111; // ADイネーブル // | +++-------1/16クロック,1/16=62.5kHz。1/128で最遅7.8125KHz。これでも電話音声程度なら十分。 // +--------------ADイネーブル while( ADCSRA & 0b01000000 ) // 変換終了待ち ; volt = ADC; volt_array[i]=volt-ref; } //シリアル送信 for(int8_t i=0;i sio_putint4(volt_array[i],0,0,0); } sio_putint4(0,0,0,111); //sending end marker } ホスト側プログラム //TwoWaySerialComm の改良版 2012/11/8 (TwoWaySerialComm2 よりこちらの方が新しい) //1. 一度に4バイトのデータ受信 //2. こちらから送信することは無いので、関連ルーチン削除 //使い方:AVR起動時は最初の数秒は先頭が1の値を送信してくるので、AVRの電源ON前に //このプログラムの実行を開始しておき、同期をとる必要がある。 import gnu.io.CommPort; import gnu.io.CommPortIdentifier; import gnu.io.SerialPort; import java.io.*; public class TwoWaySerialComm { public TwoWaySerialComm() { super(); } void connect ( String portName ) throws Exception { CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName); if ( portIdentifier.isCurrentlyOwned() ) { System.out.println("Error: Port is currently in use"); } else { CommPort commPort = portIdentifier.open(this.getClass().getName(),2000); if ( commPort instanceof SerialPort ) { SerialPort serialPort = (SerialPort) commPort; serialPort.setSerialPortParams(19200,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE); InputStream in = serialPort.getInputStream(); OutputStream out = serialPort.getOutputStream(); (new Thread(new SerialReader(in))).start(); // (new Thread(new SerialWriter(out))).start(); } else { System.out.println("Error: Only serial ports are handled by this example."); } } } /** */ public static class SerialReader implements Runnable { public static String toHex(byte digest) { StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%1$02X", b)); } return sb.toString(); } InputStream in; public SerialReader ( InputStream in ) { this.in = in; } public void run () { int int_value1,int_value2,int_value3,int_value4 = 0; byte buffer = new byte[8]; byte tmp_buf = new byte[2]; int len = -1; FileWriter fw=null; java.io.BufferedWriter bw=null; try { fw = new FileWriter("c:\\a\\log_adconv.csv"); bw = new BufferedWriter(fw); } catch (Exception e) { e.printStackTrace(); } try { //AVRから来るデータとの同期を最初にとる。先頭の値は0x0001が来るはず while (!(tmp_buf[1]==0x01&&tmp_buf[0]==0x00)) { if (this.in.available()>1) { len = this.in.read(tmp_buf); System.out.print("1st len="+len+" "); System.out.println(toHex(tmp_buf)); } } this.in.read(tmp_buf);//先頭が得られたので、残りは読み捨てておく this.in.read(tmp_buf); this.in.read(tmp_buf); System.out.println("Sync OK"); while (true) { while ( this.in.available()>7) { len = this.in.read(buffer); // System.out.print("len="+len+" "); // System.out.println(toHex(buffer)); //0xFF で&しないと、結果が2の補数になってしまう。 //参照 http://www.creativegear.jp/2011/05/09/java_byte_to_int/ /*ファイル出力ルーチン */ int_value1=(int)((buffer[0]<<8)|(buffer[1]&0xFF)); int_value2=(int)((buffer[2]<<8)|(buffer[3]&0xFF)); int_value3=(int)((buffer[4]<<8)|(buffer[5]&0xFF)); int_value4=(int)((buffer[6]<<8)|(buffer[7]&0xFF)); bw.write(int_value1+","+int_value2+","+int_value3+"\r\n"); } if (int_value4==111) break; } bw.close(); } catch ( IOException e ) { e.printStackTrace(); } System.out.println("End running"); //end func } } //end class /** */ public static class SerialWriter implements Runnable { OutputStream out; public SerialWriter ( OutputStream out ) { this.out = out; } public void run () { try { int c = 0; while ( ( c = System.in.read()) > -1 ) { this.out.write(c); } } catch ( IOException e ) { e.printStackTrace(); } } } public static void main ( String args ) { try { (new TwoWaySerialComm()).connect("COM1"); } catch ( Exception e ) { // TODO Auto-generated catch block e.printStackTrace(); } } }