google-site-verification: google3bd66dd162ef54c7.html
fc2ブログ

Arduinoで作る簡単な電圧ロガー

◆まえがき
Arduino を使った簡単な電圧ロガーを紹介します。これは、前の記事のハンディクリーナーの充電器の特性測定でも使っています。
このところ忙しくて記事の更新が停滞しているのですが、顔つなぎを兼ねて小ネタを記事にしておきます。

◆仕組み
Arduino のADコンバーターを使って電圧を測定し、結果をシリアルに流すだけのものです。CPU 内蔵の ADコンバーターなので分解能は10ビットしかありません。

シリアルに流したデーターを TeraTerm などで受信し、ログファイルに記録するような使い方を想定しています。CSV形式で出力するので Excel でそのまま読めます。

◆回路図
配線図
入力電圧(IN)をR1,R2で1/3に分圧してA0ピンに入力しています。つまりこの例での最大入力電圧は15Vです。

◆基準電圧について
ADコンバーターのフルスケール (Vref) はソフトで選択しますが、後で示すプログラムでは電源電圧である5Vを使用しています。Vref にCPU内蔵の基準電圧である 1.1V を使う手もありますが、この電圧精度がえらく悪い(確か ±0.1V)ので電源電圧を使った方がマシと判断しました。ここは異論があると思います。

基準電圧に電源電圧の5Vを使う場合、Arduino UNO ではUSBコネクタから来た5Vは電源切り替え回路を経由して供給されるのでほぼそのまま、つまりほぼ5Vになっています。しかし、もしArduino NANOを使うと、USBコネクタからの5V電源はショットキーバリアダイオードを経由して供給されるので、実際の電圧は4.8V程度に下がっているので要注意です。

なお、UNOの互換ボードなどでは前記した電源切り替え回路を省略した物があるので要注意です。ともかく、実際に+5Vピンの電圧を測定して確認しておいた方が良いです。

◆外観
外観
入力の分圧抵抗を接続し、この先に被測定物を接続します。

◆プログラム
// アナログポートの電圧をシリアルに出力  20231104_SimpleVoltageLogger
// 2023/11/04 ラジオペンチ

#include <MsTimer2.h>             // 正確な周期を刻むため、MsTimer2ライブラリを使用

#define K1        0.014648        // 1LSB当たりの電圧 (入力アッテネーター=3倍、5V x 3 / 1024)
#define INTERVAL  1.0             // 測定周期(浮動小数点指定も可能)

long n = 0;                       // 行カウンタ
float t;                          // 経過時間(秒)
volatile boolean tFlag = false;   // 割り込み発生フラグ

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Log start");                 // 開始メッセージ出力
  Serial.println(", No, sec, voltage");        // 列名出力
  MsTimer2::set(INTERVAL * 1000, MsTimer2IRQ); // 割込み周期を設定
  MsTimer2::start();                           // 割込み開始
}

void loop() {
  while (tFlag == false) {                // 割り込みフラグが立つまで待機
  }
  tFlag = false;

  Serial.print(", ");                     // タイムスタンプとの境界用のデリッミッタを出力
  Serial.print(n); Serial.print(", ");    // 行番号を出力
  n++;
  t = n * INTERVAL;
  Serial.print(t, 2); Serial.print(", "); // 経過秒数を出力
  Serial.println(analogRead(0)*K1, 3);    // 電圧値(下3桁まで)を出力
}

void MsTimer2IRQ() {                      // タイマー割り込み処理
  tFlag = true;                           // フラグを立ててタイムアップしたことを連絡
}
・解説
電圧感度はK1の値で設定
出来るだけ測定周期を正確にするため、MsTimer2 ライブラリの割り込みを使っています。なお、UNOのクロックはセラロックなので確度が悪い点は要注意。
測定周期は INTERVAL の値で設定。浮動小数点で設定可能なので半端な値も設定可能。
出力は行番号、開始からの経過時間(秒)、電圧(V)。

◆出力例
ログの例

上記は TeraTerm で受信し、タイムスタンプ付のログを記録した例です。

行の先頭にカンマを出力しているので、タイムスタンプとログの文字列が混ざらないで済みます。ちなみに、ここがくっついていると、EXCELで切り離すのに一手間必要になります。

◆まとめ
何てことないプログラムですが、毎回新しく書くのも面倒なので記事にしておきました。ちょっとした記録を取るのに使えば便利だと思いなす。

ArduinoでTimer1を使ってバーストパルスを発生させる

◆まえがき
前の記事で Arduino を使ってミニグラインダーのコントローラーを作りました。このプログラムで重要な役目を果たしたのが、PWM の波形を正確なタイミングでON/OFFさせるために作った Timer1 の割り込み処理ルーチンです。

いわゆるバーストPWM波を発生させた訳ですが、このルーチンはいろいろなことに使えそうです。ということで、このバーストPWM波形の生成の部分を抜き出して紹介したいと思います。たぶん後で何かの役に立つと思います。なお、プログラムの動作確認は Arduino UNO(互換品) で行いました。

外観

◆簡単にバーストPWM波形を発生させる方法
Arduino でバーストPWM波形を発生させたい場合、この記事で紹介する方法が一番品質の良い波形が生成出来ると思いますが、簡単にやるなら以下のような方法があります。

1)AnalogWriteを使う
AnalogWrite を使えばPWM波を出力出来るので、これをタイマー割り込みでON/OFF(AnalogWrite(0);でOFF)すればバースト波を作ることが出来ます。この方法は判り易くて簡単なので、実は前記したグラインダーの制御ソフトを作る時に最初はこの方法を用いました。

このやり方ではPWMの周波数がやや低く、(480Hzか980Hz) 分解能も1/256刻みにしか出来ないという制約がありますが、それ以外にもっと大きな問題がありました。それはPWM波形とバースト波のタイミングが固定されていないので、1周期のバースト内に含まれるPWMの山の数が変化してしまうことです。変化すると言ってもパルスの数が一つ違うことがあるだけですが、例えばフィードバック制御を行う時には余分な外乱になってしまうので好ましくありません。

2)Timer1とFlexiTimer2を使う
以前私が記事を書いた、Timer1とFlexiTimer2を使ったバーストパルス発生の実験の方法です。これなら前項の方法よりはマシになると思います。ただ失礼を承知で書くと、FlexiTimer2 はクセが強いのであまり使いたくないです。

これ以外にもいろんなやり方がありそうですが、ともかく今回私が作ったプログラムを説明します。

◆プログラム
前項のような問題が無いバーストPWM波の発生プログラムです。

・プログラム
// Timer1を使ってバーストPWM波形を発生させる 20230926_MH380_BurstPulseDeme.ino
// 2023/09/26 ラジオペンチ http://radiopench.blog96.fc2.com/

// パルスの仕様の設定(ミニグラインダーの制御用波形)
#define PULSE_N   10           // 1周期のパルス数
#define SKIP_N     2           // 停止する(歯抜けにする)パルス数
#define PERIOD    50           // パルス周期 (tm1のクロック数で指定、分周比8なら0.5usに対する倍率)
#define WIDTH     10           // パルス幅
#define INDEX_PW  10           // インデック信号のパルス幅(μs単位の値で指定、粒度は4us, 実際には5usくらい長くなる)

#define cbi(addr, bit) addr &= ~(1 << bit)  // addrのbit目を'0'にする
#define sbi(addr, bit) addr |=  (1 << bit)  // addrのbit目を'1'にする

volatile int    flagT0 = false; // バーストPWM先頭フラフ
volatile boolean outOn  = true; // true:出力ON, false:出力OFF,

void setup() {
  pinMode( 9, OUTPUT);           // PWM出力ポート
  pinMode(13, OUTPUT);           // インデックス信号

  // Timer1を fastPWMモード, プリスケーラ=8, Pin9から出力に設定
  TCCR1A  = 0b10000010;         // Timer1設定 (set: COM1A1, WMG11)
  TCCR1B  = 0b00011010;         // Timer1設定 (set: WGM13, WGM12, CS11)
  ICR1 = PERIOD - 1;            // Timer1カウント数上限(TOP値)を設定
  OCR1A = WIDTH - 1;            // パルス幅設定

  sbi(TIMSK1, OCIE1A);          // Timer1 割り込み許可(割り込み開始)
}

void loop() {
  while (flagT0 == false) {     // タイマー割り込みが入るまで待つ(PWMバーストの先頭でフラグON)
  }
  flagT0 = false;

  digitalWrite(13,HIGH);        // 同期信号を出力
  delayMicroseconds(INDEX_PW);
  digitalWrite(13,LOW);
}


ISR (TIMER1_COMPA_vect) {       // タイマー1割り込み処理。バーストPWM出力とメインへのタイミング通知
  static int cycle = 0;
  if (cycle == 0) {             // サイクルの先頭だったら、
    flagT0 = true;              // フラグを立ててメインに通知
  }
  if (cycle < SKIP_N) {         // 先頭のスキップ回数以内だったら、
    cbi(TCCR1A, COM1A1);        // PWM出力停止
  } else {
    if (outOn == true) {        // 出力フラグONなら
      sbi(TCCR1A, COM1A1);      // 先頭以外はPWM出力ON
    }
  }
  cycle++;                      // 次回に備えカウントアップ
  if (cycle >= PULSE_N) {       // 1サイクル分のパルス出したら先頭に戻す
    cycle = 0;
  }
}
設定を順に書くと、
・まずは setup の22,23行目の TCCR1A, TCCR1B で Timer1 を Fast PWMモード、クロックを2MHz(8分周)、Pin9~出力するように設定。
・PWM の周期は PERIOD で指定 (50*0.5us=25us)
・PWM のパルス幅は WIDTH で視程 (10*0.5us=5us)
・バーストの周期は PULSE_N で指定するパルス数で指定 (10*25us=250us)
・バーストパルスを歯抜けにする数は SKIP_N で指定 (2)

Timer1 を使っているので上記の PERIOD と WIDTH の値は16ビットまで指定可能なはずです。なお、当然ですがPERIOD>=WIDTH。

割込み処理ルーチン(ISR (TIMER1_COMPA_vect))で、パルスカウントにもとづき出力の要否を決定してバーストPWMを出力しています。

・実行結果
上記プログラムを実行した時の出力波形は以下のようになります。
テスト波形
上の波形が Index信号(Pin13)、下が PWM出力(pin9)です。
前項で説明した通り、1周期10パルスのうち先頭の2パルスが無い波形、つまりバースト波形が出ています。

◆ミニグラインダーの波形例
ミニグラインダーで使った条件のパルスを発生させてみます。プリスケーラーは8のままで、
PULSE_N=200, SKIP_N=9, PERIOD=400, WIDTH=100 に設定した場合の波形です。

・全体
ミニグラインダー波形全体
周期40msのバースト波が出ています。

・先頭部
グラインダー波形先頭部
先頭の9発がスキップされています。実際のプログラムではこのスキップした期間の間に誘導電圧の測定を行っています。

◆超音波レーダーの波形例
面白そうなので超音波レーダーの探査音を発生させるプログラムを作ってみました。0.1秒周期で周波数40kHzのバースト波を10発発生させています。0.1秒なら音速の往復で約170mまでの範囲の物体の検出が出来るはずです。

プログラムとしては、PULSE_N=4000, SKIP_N=3990, PERIOD=50, WIDTH=25 に設定しています。

・全体波形
超音波センサー
4000パルスのうち3990パルスを止め、残る10パルスだけ出力しています。100ms毎に出力されていることは判りますが、このスケールでは細部までは判りません。

・先頭部拡大
波形説明
オシロは下側のPWM波でトリガしています。40kHzで10発出力した後にインデックス信号が出ています。このタイミングから受信を開始すればレーダーが作れると思います。(簡単では無さそうですが、、)

◆まとめ
こういう設定はCPUのデーターシートをよく読まないと出来ませんが、この記事のように動作確認プログラムとセットでまとめておけば、すぐに使えて便利だと思います。

この記事を書いていて思い付いたのが、Arduinoを使った磁気浮上(吊り下げ)の実験です。この実験ではコイルに通電した状態で外部の磁石の磁界検出をやっていますが、自分で通電して磁界を発生させている状態で外部磁界の検出をやるのは、ちょっと無理がある気がします。そんなことしなくても、このプログラムを使ってコイルの通電を一旦停止し、その間に素早く外部磁化の検出をやってしまえば検出精度が上がって浮上の安定性が高まるかも知れません。時間のある時にやってみたいと思います。

この記事ではTimer1のプリスケーラーの分周比を8にして実験していますが、他の倍率も試すと良いと思います。但し、PWMの周期を極端に短くすると割り込み処理の ISR (TIMER1_COMPA_vect) が追いつかなくなって処理が破綻するかも知れません。ひょっとしたら多重に割り込みが入ってハングアップしてしまい、チップイレースかけて初期化しないとプログラムの書き込みが出来なくなってしまうかも知れませ。このあたりどうなんでしょうね。

磁気浮上(吊り下げ)の実験をちょっと改良

◆まえがき
前の記事でやったArduinoを使った磁気浮遊を少し改良したので紹介します。ちなみに、もっと安定な物にしたかったのですが、これ以上改良できなかったのであきらめました。

◆浮遊体の改良
浮遊状態が安定し難い原因の一つとして、磁石の先端が横方向に振れた時に中央に戻す力、つまりセンターリング力が弱いことがあります。原因としては、磁石の先端が平坦なので磁界分布も平坦に近くなっているのが良くないのではないかと考えました。

そこで、磁石の先端に円錐形の鉄片を追加することで磁界の分布が尖るように改良してみました。

・例-1
20230818IMG_4431.jpg
右端の円錐形の鉄片を追加しました。なお、この鉄片は接着剤で固定してあります。

この鉄片は皿ビスの根元の部分を加工して作った物で、最終的な形状は、3.5インチHDDを改造して作ったグラインダーを使って削って調整しました。なお、先端を尖らすとぶつかった時にホール素子にダメージを与える可能性があるので、ゆるやかな球面にしてあります。

・例ー2
20230818IMG_4432.jpg
別の磁石に取り付けた例です。

◆浮遊状態
20230818IMG_4430.jpg
先端が左右に振れ難くなって安定度が増しました。

◆プログラムの改良
ADCのクロックを高速化してみました。クロックが32倍速くなったので振動に対するレスポンスが良くなることを期待しています。
// 磁気吸引力による浮上(吊り下げ)File:20230809_MagLevitation.ino
// ADC高速化
// 2023/08/09 ラジオペンチ http://radiopench.blog96.fc2.com/

#define  HV1 435   // コイルOFF時の閾値(磁界がこの値以上になったらコイルに通電)
#define  HV2  10   // コイルON時の閾値変化量(この値だけ減らす)

int x;
boolean m = false;           // 制御モードフラグ(false:コイルOff時, true:コイルOn時)

void setup() {
  ADCSRA = ADCSRA & 0xf8;
  ADCSRA = ADCSRA | 0x04;    // ADCクロック分周比を128→16に変更
  pinMode(2, OUTPUT);        // コイル励磁ピン
}

void loop() {
  x = analogRead(0);          // 磁界の強さを測って
  if (m == false) {           // コイルOFF時で、
    if ( x > HV1) {           // 磁界がOFF時の閾値以上だったら、(磁石が上に来ていたら)
      digitalWrite(2, HIGH);  // コイルを励磁(磁界を打ち消して押し下げる)
      m = true;               // モードフラグをコイル励磁状態にセット
    }
  } else {                    // コイルON時で、
    if ( x < HV1 - HV2) {     // 磁界がON時の閾値以下(磁石が下)だったら、
      digitalWrite(2, LOW);   // コイルOFF(磁界の打ち消しを止めて引き上げる)
      m = false;
    }
  }
  // 上記以外のケースは現状維持
}
たいした変更では無いのでリンクの形で掲載しても良かったのですが、後でここの設定を使い廻すことがありそうなので、見える形で公開します。

◆まとめ
以上のような改良で安定性が増しました。

本当はもっと改良したくて、コイルの電流をPWMで可変にし、位置誤差に対する比例制御をやってみたのですがうまく行きませんでした。PWMの励磁とADCのタイミングを同期させなかったのが敗因だと思います。このあたり、時間があれば再度調整してみたいと思います。ただ、現在のコイルはインダクタンスが大きくて高速な制御には向いていないので、大幅に改善することは難しいキモします。

カレンダー
11 | 2023/12 | 01
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。
記事の内容のご利用は読者の自己責任でお願いします。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード