google-site-verification: google3bd66dd162ef54c7.html

Arduinoを使ったパルスジェネレーターの製作、(ソフト解説編)

 少し間が空いてしまいましたが、Arduinoを使ったパルスジェネレーターの製作記事の続きです。今回が最終回で、ソフト解説編です。このパルジェネはほとんどの機能をソフトで作っているので、この記事は実質的には機能解説編になります。

▼電源投入前
電源OFF
 右下のロータリーエンコーダと3つのタクトスイッチを組み合わせて操作を行います。

▼動作中
動作中の画面
 電源を入れるとEEPROMから設定値を読み出してパルス出力を開始します。液晶には出力中の波形のパラメーターを表示します。

 画面左上のNは、パルスの極性を表わしており、Nはネガという表示です。(ポジなら P 表示)
 0.04ms/0.08msの表示は、左側の数字がパルス幅、右側がパルス周期を表わしています。
 下段左はパルスのデューティ比、下段右はパルス周波数です。

 パルス幅と周期は 99.99ms、999.9ms、9.999s の3つのレンジで表示します。実際に出力されるパルスはこの表示と一致しています。つまり、レンジ切り替えなどで端数が出た場合は表示の分解能で丸めています。

 なお、時間設定の最小分解能は0.02ms。パルス周期-パルス幅 =< 0.02ms という条件で入力可能な値を制限しており、制限を越えた設定は無視するようになっています。Arduinoの時間の最小ステップは4μsで、これを丸め誤差無しで表示するために20μを最小分解能としました。四捨五入して10μs分解能にする手も考えたのですが、気持ち悪いのでやめました。

▼表示例
動作中
 時間の最小分解能は0.02msなので、このレンジでの最大パルス幅は99.98msです。(99.99msは設定出来ない)。なお、パルス周期は999.9msになっています。

▼値の変更
早送り
 ロータリーエンコーダー(以下、略してダイヤル)で設定値の変更を行います。設定出来るのはパルス幅と周期で、ダイヤルを早く廻すと数値が早送りされます。またFastボタンを押しながらダイヤルを廻すと爆速で値が変化します。なお、値の変更はレンジをまたいでシームレスに行うことが出来るので、直感的な操作が可能です。

▼ダイヤルで変更する値の選択
パルス幅/周期切り替え
 Pw/Ppボタンを押すと下段の表示がこのように変わり、現在の変更対象を表示します。この表示はパルス幅の変更モードになっていることを表わしています。

 ダイヤルを廻すと、変更対象を変えることが出来ます。

パルス幅/周期切り替え
 この表示はパルス周期変更モードです。↑で変更対象の数値を示しています。

▼出力極性変更
N/P
 Poralityボタンを押す度にパルス巣の極性が反転します。設定状態は液晶の左上に P/N の文字で表示。

▼設定値の保存
EEPROMに設定保存
 Fastを先に押してPw/Ppボタンを同時に押すと現在の設定(パルス幅/周期/極性)をEEPROMに保存します。ここで保存した内容は次回電源ONの際に読み出されて反映されます。

▼デューティ50%設定
Duty50%設定
 Fastを先に押してPoralityを同時に押すと、デューティが50%となるようにパルス幅を変更されます。なお、20μsステップの値しか設定出来ないので、周期のぴったり半分のパルス幅にならない場合があります。(Ex:周期100μsの場合のパルス幅は50μsでは無く60μsが設定される)

 Tips:ダイヤルを廻してパルス周期を小さくしようとしても、パルス幅以下に小さくすることは出来ません。こういう場合は、Pw/Ppボタンでパルス幅を先に小さくしないといけません。でもデューティ50%ボタンを押せば、パルス幅をすばやく半分に出来るので、場合によってはこっちの方が便利です。

 全プログラムはこちら (拡張子がtxtになっています)

 以下、プログラムの主要部についてポイントを解説します。

◆メインループ
void loop() {
duty50(); // ボタンが押されていたらパルス幅を50%デューティにする
EepSave(); // ボタンが押されていたらEEPROMへ設定内容を保存する
setPorarity(); // パルス極性設定
mode = ModeSelect(); // ロータリーエンコーダー設定する対象の選択(パルス幅or周期)
X = ReadEnc(); // ロータリーエンコーダーを読む
switch (mode) {
case 0: // パルス幅設定
IncStep = DecideStep(t1);
t1 += X * IncStep;
t1 = ResoADJ(t1); // 有効数字を補正
if (t1 < 40) { // t1(パルス幅)が40μs以下なら
t1 = 40; // t1の下限は40μs
}
if ((t2 - t1) < 40) { // t2とt1の差が40μs以下なら
t1 = t2 - 40; // t1の上限はt2から40μs下
}
fix4Disp(1, 0, t1); // パルス幅表示
break;

case 1: // パルス周期設定
IncStep = DecideStep(t2);
t2 += X * IncStep;
t2 = ResoADJ(t2); // 有効数字を補正
if ((t2 - t1) < 40) { // t2とt1の差が40μs以下なら
t2 = t1 + 40; // t2の下限はt1より40μs上まで
}
if (t2 > 10000000UL) {
t2 = 9999000UL; // t2の上限は9.999s
}
fix4Disp(9, 0, t2); // パルス周期表示
break;
}

t3 = t2 - t1; // OFF時間計算

if (X == 0) { // エンコーダーが変化していなかった場合だけ
duty1000 = (t1 * 100) / (t2 / 10); // t1*1000/t2ではオーバーフローするので、
DispDuty(duty1000); // デューティ値を表示
freq100 = 100000000 / t2;
DispFreq(freq100); // 周波数を表示
} // ここを毎回動かすとエンコーダーのレスポンスが悪化
}
 このループで操作の検出とパルス出力条件の変更および表示を行っています。ループを廻る速度が遅いとロータリーエンコーダーの変化を取りこぼしてしまいます。そこで、更新が必要な箇所だけ表示の更新を行っています。

◆ロータリーエンコーダー読取り部
int ReadEnc() {             // ロータリーエンコーダーの読取り
static byte bp = 0;
int R = 0;
static unsigned long LastChangeTime;
unsigned long TimeDelta;
bp = bp << 1;
if (digitalRead(ECA) == HIGH) { // A相の状態を
bp |= 0x01; // bpの末尾に記録
}
bp = bp << 1;
if (digitalRead(ECB) == HIGH) { // B相の状態を
bp |= 0x01; // bpの末尾に記録
}

bp = bp & 0x0F; // 下位4ビット残して上位を消す
if (bp == 0b0111) { // このビットパターンと一致していたら
R = 1;
}
if (bp == 0b1011) { // このビットパターンと一致していたら、
R = -1;
}
if (R != 0 ) {
TimeDelta = millis() - LastChangeTime;
if (TimeDelta < 50) { // すごく短い間隔で操作されていたら
R *= 20; // 20倍速
} else if (TimeDelta < 70) { // 短い間隔だったら
R *= 5; // 5倍速
}
LastChangeTime = millis();
}

if (digitalRead(8) == LOW) { // 早送りボタンが押されていたら
R *= 50; // 更に50倍速
}
return R;
}
 ビットパターンの一致を見てダイヤルの操作を検出する方式になっています。後ろの方が早送りの処理ですが、操作感を良くするにはもう少しチューニングした方が良さそうです。また、ロータリーエンコーダーの種類によっても最適な条件は変わりそうです。

◆パルス発生部 
//  タイマー割り込みでパルス出力
void T1Isr() {
if (PosiPulse == true) {
PORTB |= B00100000; // sbi PORTBのビット5(D13ピン)
} else {
PORTB &= B11011111; // cbi (digitalWriteよりこの方が高速)
}
Timer1.setPeriod(t1); // パルス幅セット
Timer1.attachInterrupt( T2Isr ); // タイムアップでT2Isr
}

void T2Isr() {
if (PosiPulse == true) {
PORTB &= B11011111; // cbi
} else {
PORTB |= B00100000; // sbi
}
Timer1.setPeriod(t3); // 次のパルス
Timer1.attachInterrupt( T1Isr ); // タイムアップでT1Isrに戻る
}
 ここが心臓部で、パルス発生はこのルーチンで行っています。

 ポートを直接操作しているのは、少しでも高速化したかったためです。ビットパターンで直接操作するのは移植性が悪くなると言われているので、この書き方はちょっと邪道かも知れません。

 それと、こんなふうにキャッチボールするプログラムではなく、定周期割込みを常に発生させておき、その割り込みを使ってパルス幅の割り込みを起動するようなプログラムを作ればパルス周期の精度が上がるはずです。このあたりはもう少し工夫する余地があるかもしれません。

◆まとめ
 マイコンを使った物はソフトを書くだけでいろいろな機能を作り込む事が出来ます。使用頻度の低い機能はボタンの押し方の組み合わせで起動させるようにすると、少ないボタンの数でいろいろな機能を選択出来るようになります。ソフト開発の最後はこういうおまけの機能を作りこむのですが、これをやってる時が一番楽しい時期です。

 ということで、とても長い記事になってしまいましたが、これでArduinoを使ったパルスジェネレーター作りの話は終わりです。

 なお、一連の記事はパルスジェネレーターのカテゴリにまとめました。
関連記事

コメントの投稿

管理者にだけ表示を許可する

こんにちは!

いつも的確で貴重なアドバイスをいただき感謝申し上げます。
奇遇にも、昨夜全自動洗濯機の分解情報を捜し求めて辿り着いたのが、ラジオペンチさんのブログだったのです。
しかもナショナル・ニューロファージィー!
これ以上の情報はありません。感謝です。

re:こんにちは!

ロートレーさん、今晩は

全自動洗濯機の洗濯槽外しをやられるのでしょうか。私の情報が役に立てば幸いです。ロートレーさんならいろんな工具をお持ちなので、構造さえ判っていれば楽勝かと。

あと、そちらのブログに書いたULPのシナ合板の件、詳しい解説ありがとうございます。てっきり翼面に使うのかと思っていました。早トチリですみません。

作りました。

どうも、ご無沙汰しております。
と言っても、いつも楽しみにブログは拝見させてもらってます。
久しぶりに電子工作してみました。
パルスジェネレータを持っていなかったので、なんか得をした気分です。
いつもながら、作成説明が分かりやすいので問題なく作れました。 昨日、完成したばかりなのでこれからいろいろ遊んでみたいです。
乾電池2本で作ったので、DCDCコンバータHT7750Aとarduinoマイコンからのスパイクノイズとノコギリ状のノイズがちょっと気になります。
このノイズをある程度平滑にするのって難しいのですか。
lcdのコントラストにダイオードを使うテクニックは便利ですね。コストダウンも出来て素晴らしい。

re:作りました。

hiroさん、お久しぶりです。

そうやって同じ物が出来たよ、という話を聞くと嬉しいです。

電池2本から昇圧されたんですね。ノイズは電源間のデカップリングコンデンサを増やすのが常套手段ですが、アースの配線の引き回し方でも問題が出ます。
電池のマイナスとロジック系のGNDがHT7750AのGNDピンのところで一点アースになるように配線するといいのですが。

ノイズ対策

アースラインを基板全体を囲むように外周に這わしていました。
いろいろな所からアースが取れるし、アース強化するだろうと思っていたのですが、最悪なことをしていたんですね。
どおりで、ADコンバーターなどブレッドボードでテストした方が数値が落ち着いていることが多々あったのも納得します。 早速、配線の見直ししてみます。
アドバイスしてもらって助かりました。これから気をつけなくては。
進歩ない初心者、丸出しで恥ずかしい。

パルスジェネレーターの波形を見ていたら、P1msec.9.18~9.20msecやP10msec・18.18~18.20msecやP100msec.108.1~108.2msec
となにかパルスがズレるのですが、
特に、私には何も影響はないのですが、パルス幅が変わるので気になりしまた。
0.04msec 8.22~8.24secの時が、大きな誤差でした。

プログラムを理解してないので、ただ気になったことを書いてみました。処理上当たり前な事を、書いていたとしたらごめんなさい。

これから、雷の季節に備えて、去年秋月で買った雷センサーをもう一度作り直してみようと思います。
ノイズエラーや誤作動などうまく動作していなかった。
アースの件を、教えていただいたので、やる気が出てきました。上手くいくと良いな。

re:ノイズ対策

アースの件については「一点アース」などのキーワードで検索するといろいろ解説が出てくると思います。
このあたりは回路図で表現しずらいですが、配線する時に電流の流れを意識して接続するようにしています。もちろん大きな電流が流れるとこだけですが。

あと、特定の値を通過する時にパルスの時間変化が滑らかになっていない件、こちらでも再現しました。
これは想像ですが、パルスを作るのに割り込みを使っているのですが、その割り込みとArduinoがシステムとして使っている割り込みとのタイミングの関係で、処理時間にムラが出ちゃっているのだと思います。
これはちょっと簡単には解決出来そうにないです。実は、この記事↓
http://radiopench.blog96.fc2.com/blog-entry-653.html
はそういう現象の解決にもなる方法なんですが、その後進展していません。

ノイズ対策は難しい

あれから、回路を見直しデカップリング゛・1点アースを目標に作り直しました。
完全な1点では無いのですが、だいぶノイズが少なくなりました。もっと欲が出てきてしまいどうしたらもっと平滑になるか調べてたらインダクタとコンデンサでローパスフィルタで改善すると書かれていたので、計算がわからないので適当に試してみましたが、逆にノイズが増えたり脈打ったりで、ノイズ対策は本当に難しいとつくづく思いました。
一般に言うスイッチングノイズって、消せるのですか?
マイコンノイズ対策のテクニックの記事あったら良いです。

パルス幅のズレの件は了解しました。
割り込みが関係していたのですね。

re:ノイズ対策は難しい

hiroさんおはようございます。

コイルを入れるのは電源のノイズ対策として有効だと思います。ただ部品の選定がけっこう難しいように思います。インダクタンス、直流抵抗、通過電流、コアの周波数特性、漏洩磁束などです。

HT7750AなどのDCDCコンではある程度のリップルが残るのは仕方ないと思います。それに負荷が軽くても重くてもリップルが多くなる特性だったような気がします。

高級オーディオ機器にスイッチング電源が使われないのはそんな理由ではないかと思います。

高精度化

こんにちは。
欲しい機能、性能をきちんと考慮した作例は元気が出ますし、とても参考になります。
古い記事へのコメントでごめんなさい。
本記事を参考にして、OC1Bの高速PWM出力を使って高精度化できました。
パルス幅、周期の粒度は1/16MHzとなります。AVR内蔵のハードのみでパルスが出力されますので、これらの変動は非常に小さいです。
バースト波出力など機能の自由度は本記事のソフトウェア・ピンポン方式が勝りますが。
時間ができたら当方のブログで詳細を説明したいと思います。
(スペル、気になりました。すみません。seve->save、porarity-> polarity)

re:高精度化

vabenecosiさん、コメントありがとうございます。

なるほど、そういう手があるんですね。タイマーのレジスタはあまりいじったことが無いので、私にとって未知の領域です。
具体的にどうやられたのかすごく興味があるます。そちらのブログの記事が出来たらぜひ教えてください。

あと、スペル間違ってますね、いや恥ずかしい。

vabenecosiさんへ追記

替わったハンドルネームだったので検索させて頂いたら、「アキバ通いと旅」の中の方ですよね。

以前、「アルデュイーノ」は止めようよ」という記事を書かれたと思いますが、私も全く同じ意見です。

以前イタリアからの来た研修生がいたのでArduino の発音を聞いたことがありますが、私にはアルディーノと聞こえました。ともかくトラ技は変な発音を書くな!と言いたいです。

で、そういうふうに言葉を大切にされる方なので、私のスペルミスが時に気になったんでしょうね。今後注意したいと思います。

ちなみに私は言葉にする時は、イントネーション無しで、「あるでぃーの」と呼んでます。

re:高精度化

「Va bene cosi」はイタリア語で「これでいいのだ」という意味です(バカボンのパパのセリフですね)。
実はパルス発振器はまだプログラムの推敲中です。周期のレンジに合わせて、設定できるパルス幅の分解能を変える部分で試行錯誤しています。例えば設定できる最大の周期はカウンタクロックのプリスケーラを1/1024に設定して65536 / (16MHz / 1024) = 4.194304 sec、設定できる最小パルス幅は64μsになり、プリスケーラなしとするとそれぞれ4.096ms、62.5nsとなる、という事情があるのです。
ということで記事を書くのがいつになるか分からないので、ごく簡単にパルス発生のカラクリを説明しておこうと思います。
1. timer1に比較一致値OCR1AをTOP値として高速PWM動作をさせる。BOTTOM(=0)からTOP値までカウンタ(TCNT1)がカウントを繰り返すようにレジスタを設定する。OCR1Aでパルスの周期を設定することになる。
2. PWM出力(OC1B)はカウンタ値がBOTTOM(=0)になった時Highになり、もう一つの比較一致値OCR1Bを横切るときにLowとなるようにレジスタを設定する(その逆の設定も可 -> パルスの極性を変えられる)。OCR1Bでパルス幅が設定できる。
3. 広い周期、パルス幅のレンジをカバーするためにプリスケーラを切り替えることでカウンタに供給するクロック周波数を変える。
ロータリエンコーダ入力ピンはOC1Bとバッティングするので要引っ越しです。

当方のブログは2か月ほどほったらかしています。ラジオペンチさんが訪問されることもあるのなら止めないで続けようかなとも思います(電子工作に費やす時間を少しブログに割かなくては)。では、では。

re2:高精度化

vabenecosiさん、解説ありがとうございます。

AVRのタイマーカウンターまわりは細かいところまでいじったことが無いので勉強になります。

レジスタの設定で決まるパルスの条件を、いかにして人間が判り易く設定や表示させるかがキモになるんでしょうね。
設定さえ出来ちゃえば、ハードで自動的に波形が出るのでジッタとか無い綺麗なパルスになるんですね。

re2:高精度化

人間が判り易いプログラムになっているかどうかは分かりませんが、一応の目処がつきました(温泉旅館の新館・別館増築みたいな追加、修正部分が多々あります)。
ついてはDropboxか、電子メール添付かの手段でプログラムを共有したいと思うのですが、いかがですか?

re3:高精度化

vabenecosiさん、今晩は

すみません、読者の方とメールなどによるやり取りは原則行っていません。これは個人情報を出来るだけ出さない、などの理由によるものです。

面倒臭いやつだと思われるかもしれませんが、ご理解下さい。

ソースファイルでしたら、vabenecosi さんのブログにデーターを上げていただいて、そのURLをコメントで連絡していただく手があります。(データーをアップロードするだけで記事は書かなくても大丈夫です)
ただこの場合は不特定多数の方から見える状態になってしまいます。

re:高精度化

ポリシー了解しました。
いずれブログに記事を書きますのでお待ちください。
少しの時間ですが使ってみています。自画自賛かもしれませんが、ピシっとブレのないパルス幅、周波数のパルスが出るのは気持ちが良いです。具体的な使い道は今は思い浮かびませんが極端なデューティのパルスが出るのも面白いですね。

参考までにスケッチの冒頭のコメント部分(スペック等を書いた部分)をコピペしておきます。

* 簡易パルスジェネレーター(timer1、高速PWM動作、OC1B出力利用)
* パルス設定範囲 パルス幅:1μs~4.194s、周期:2μs~4.194s
* 割り込み不使用
* OC1A、OC1Bを使うのでロータリエンコーダ入力はA0、A1へ引っ越し
* マイコン:Arduino Uno相当、クロック16MHz
* スケッチが プログラムストレージ領域の 9,818バイト (30%) を使用
* グローバル変数が 345バイト (16%) の 動的メモリを使用

re:高精度化

vabenecosiさん、了解です。

どういう実装になっているのか興味があります。でも、ゆっくりやっていただければと思います。

re:高精度化

こんにちは。
今日アキバに筐体実装に必要なパーツを買い出しに行こうと思っていましたが、道路に雪が残っていて寒く(自転車で行くのでとりわけ)危険なこともあり、幸か不幸かブログ記事を書くことにしました。
おかげで速報版としてパルスジェネレータの最初の記事をブログにアップすることができました(久しぶり故なのか記事を書くのはやはり億劫です。何か反応あれば払拭できるでしょうけど)。
プログラムもアップしているのでご覧になられて何かご意見をいただければ幸いです。筐体組み込みについては電源やスイッチ、表示デバイスの配置について私なりの妙な?流儀がありますので(買い出し後の)続報で紹介させていただきたいと思います。

re4:高精度化

vabenecosi さん今晩は

そちらの下記の記事を拝見しました。
http://vabenecosi.blog.fc2.com/blog-entry-32.html

またプログラムの、Simplified_PG_v1.txt も拝見してます。うちのArduino IDE(V1.8.1) でコンパイルが通ることを確認しました。

ずいぶん前に書いたプログラムなので細かいところはほとんど忘れてますが、私の書いた部分を出来るだけ残すようにコードを書いていただいて恐縮です。

ブレッドボードに回路を作って実際に動かしてみればいいのですが、すぐには出来そうにありません。ともかくご連絡しておきます。

なお、まだ記事が続くようですのでその先のコメントは、vabenecosi さんのブログの方に入れさせていただきます。

re:高精度化

こんばんは。
お騒がせしております。
当方のブログネタの進捗状況についてですが、停滞しているというべきか、進展したというべきか、今更ながらのロータリエンコーダの入力について拘泥中です。
ガジェットとしての完成には今少し時間がかかりそうです。

re5:高精度化

すみません、この記事に書いた状態ではロータリーエンコーダーを読み飛ばすことがあって、操作感が悪いです。

そのあたりは、vabenecosiさんのブログにコメント入れておきました。

ハード編アップの件

こんにちは。

既に訪問して頂いているかもしれませんが、ブログにパルスジェネレータのハード編をアップしております。
気になっているのが、当方の修正点の説明のため一部とはいえラジオペンチさんの回路図を引用していることです。
言葉のみでも説明は可能ですので取り下げるかべきか、とも思っています。
どのように感じておられるか、コメントを頂ければ幸いです。

re:ハード編アップの件

vabenecosi さん、今日は。

ハード編拝見してます。
http://vabenecosi.blog.fc2.com/blog-entry-36.html
他の方の作り方を見るのはとても参考になります。

回路図の使用については、出展元を明記していただいているので全然問題ありません。こんな具合に回路図をカストマイズされる読者の方がいらっしゃるのなら、回路図ファイル(BSch)も公開しておいた方が良かったかも、と思いました。

re:ハード編アップの件

有難うございます。
ではそのままにさせていただきます。

話は変わりますが、昨日から秋月でRaspberry Pi Zero WHが販売されていますね。天候のせいか、価格コントロール効いていてどこも同じ価格だからか、売れゆきはボチボチです。
私自身は組み込み系の応用にはちょっと高いかなと感じて自制しています。

re:ハード編アップの件

vabenecosiさん了解です。
記事をネットに公開している以上、何をされても文句言わないというスタンスでやっています。あまりにひどいのは別ですが、

あと、秋月でRaspberry PI Zero WH てのが出てるんですね、知りませんでした。今見てきましたが、値段が高いのでびっくりです。
カレンダー
06 | 2018/07 | 08
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コード